pax_global_header00006660000000000000000000000064147414734730014530gustar00rootroot0000000000000052 comment=043343d2df7b09d1938bc3dc313d86a96be457cc OpenSC-0.26.1/000077500000000000000000000000001474147347300127455ustar00rootroot00000000000000OpenSC-0.26.1/.appveyor.yml000066400000000000000000000202061474147347300154130ustar00rootroot00000000000000version: 0.26.1.{build} platform: - x86 - x64 configuration: - Release - Light environment: GH_TOKEN: secure: jeReA6BNx/dXVMGfroKadgE9ByKAE/tAGcb2z+dIVZGAN29X1Pu22wi1TuVOy9ZugqBzjvFV4knwHJSGi0+U6Yj1fTfa2CYpeCBym4JOXqPis/GpKfSeBV9IrmJGT/Av PATH: C:\cygwin\bin;%PATH% OPENPACE_VER: 1.1.3 ZLIB_VER_DOT: 1.2.12 matrix: # not compatible with OpenSSL 1.1.1: # - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013 # VCVARSALL: "%VS120COMNTOOLS%/../../VC/vcvarsall.bat" #- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 #VCVARSALL: "%VS140COMNTOOLS%/../../VC/vcvarsall.bat" #DO_PUSH_ARTIFACT: yes - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 VCVARSALL: "%ProgramFiles(x86)%/Microsoft Visual Studio/2017/Community/VC/Auxiliary/Build/vcvarsall.bat" DO_PUSH_ARTIFACT: yes # not compatible with WiX 3.11.2: # - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 # VCVARSALL: "%ProgramFiles(x86)%/Microsoft Visual Studio/2019/Community/VC/Auxiliary/Build/vcvarsall.bat" 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 - ps: $env:PACKAGE_NAME=(git describe --tags --abbrev=0) - ps: >- If ($env:Platform -Match "x86") { $env:OPENSSL_PF="Win32" $env:ARTIFACT="OpenSC-${env:PACKAGE_NAME}_win32" } Else { $env:OPENSSL_PF="Win64" $env:ARTIFACT="OpenSC-${env:PACKAGE_NAME}_win64" } - ps: >- If ($env:Configuration -Like "*Light*") { $env:ARTIFACT+="-Light" } Else { $env:NMAKE_EXTRA+=" OPENSSL_DEF=/DENABLE_OPENSSL OPENSSL_DIR=C:\OpenSSL-v111-${env:OPENSSL_PF}" $env:NMAKE_EXTRA+=" OPENSSL_EXTRA_CFLAGS=/DOPENSSL_SECURE_MALLOC_SIZE=65536" 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" } } If (!(Test-Path cpdksetup.exe )) { appveyor DownloadFile "https://download.microsoft.com/download/1/7/6/176909B0-50F2-4DF3-B29B-830A17EA7E38/CPDK_RELEASE_UPDATE/cpdksetup.exe" } - echo "Using %APPVEYOR_BUILD_WORKER_IMAGE% with %VCVARSALL%" - call "%VCVARSALL%" %Platform% - cpdksetup.exe /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 nmake /nologo -f win32/Makefile.msc 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" 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 # OpenSSL 1.1.0 #cl /nologo /IC:\OpenSSL-v110-${env:OPENSSL_PF}\include /I. /DX509DIR=\`"/\`" /DCVCDIR=\`"/\`" /W3 /D_CRT_SECURE_NO_DEPRECATE /DWIN32_LEAN_AND_MEAN /GS /MT /DHAVE_ASN1_STRING_GET0_DATA=1 /DHAVE_DECL_OPENSSL_ZALLOC=1 /DHAVE_DH_GET0_KEY=1 /DHAVE_DH_GET0_PQG=1 /DHAVE_DH_SET0_KEY=1 /DHAVE_DH_SET0_PQG=1 /DHAVE_ECDSA_SIG_GET0=1 /DHAVE_ECDSA_SIG_SET0=1 /DHAVE_EC_KEY_METHOD=1 /DHAVE_RSA_GET0_KEY=1 /DHAVE_RSA_SET0_KEY=1 /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 ssl_compat.c # OpenSSL 1.1.1 cl /nologo /IC:\OpenSSL-v111-${env:OPENSSL_PF}\include /I. /DX509DIR=\`"/\`" /DCVCDIR=\`"/\`" /W3 /D_CRT_SECURE_NO_DEPRECATE /DWIN32_LEAN_AND_MEAN /GS /MT /DHAVE_ASN1_STRING_GET0_DATA=1 /DHAVE_DECL_OPENSSL_ZALLOC=1 /DHAVE_DH_GET0_KEY=1 /DHAVE_DH_GET0_PQG=1 /DHAVE_DH_SET0_KEY=1 /DHAVE_DH_SET0_PQG=1 /DHAVE_ECDSA_SIG_GET0=1 /DHAVE_ECDSA_SIG_SET0=1 /DHAVE_EC_KEY_METHOD=1 /DHAVE_RSA_GET0_KEY=1 /DHAVE_RSA_SET0_KEY=1 /DHAVE_EC_POINT_GET_AFFINE_COORDINATES=1 /DHAVE_EC_POINT_SET_AFFINE_COORDINATES=1 /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 ssl_compat.c # OpenSSL 1.0.2 #cl /nologo /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 ssl_compat.c lib /nologo /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 ssl_compat.obj cd ${env:APPVEYOR_BUILD_FOLDER} } $env:NMAKE_EXTRA+=" OPENPACE_DEF=/DENABLE_OPENPACE OPENPACE_DIR=C:\openpace-${env:OPENSSL_PF}" } - bash -c "exec 0- Get-ChildItem -recurse ${env:APPVEYOR_BUILD_FOLDER} -exclude vc*.pdb *.pdb | % { 7z a -tzip ${env:ARTIFACT}-Debug.zip $_.FullName } - appveyor PushArtifact %ARTIFACT%.msi - appveyor PushArtifact %ARTIFACT%-Debug.zip # keep in sync with .travis.yml - bash -c "git config --global user.email 'no-reply@appveyor.com'" - bash -c "git config --global user.name 'AppVeyor'" - bash -c "if [ \"$DO_PUSH_ARTIFACT\" = yes -a -z \"$APPVEYOR_PULL_REQUEST_NUMBER\" -a \"$APPVEYOR_REPO_NAME\" = \"OpenSC/OpenSC\" ]; then .github/push_artifacts.sh \"AppVeyor build ${APPVEYOR_BUILD_NUMBER}.${APPVEYOR_JOB_NUMBER}\"; fi" deploy: - provider: Webhook url: https://app.signpath.io/API/v1/8d2463fe-39bd-4a41-bb72-f008b4b1fe17/Integrations/AppVeyor?ProjectSlug=OpenSC&SigningPolicySlug=release-signing&ArtifactConfigurationSlug=initial authorization: secure: 73Qvk9MbD7M7ZcoO6NnSH7zDA+9wSKZqNc602b1UeA5v6GsDyEvpCOlzJ9VvBcUuePTKBRsZ+uGQYKsddABgQA== cache: - 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 - cpdksetup.exe -> .appveyor.yml OpenSC-0.26.1/.clang-format000066400000000000000000000021341474147347300153200ustar00rootroot00000000000000BasedOnStyle: 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 OpenSC-0.26.1/.editorconfig000066400000000000000000000004471474147347300154270ustar00rootroot00000000000000root = 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 OpenSC-0.26.1/.github/000077500000000000000000000000001474147347300143055ustar00rootroot00000000000000OpenSC-0.26.1/.github/.codeql.yml000066400000000000000000000003461474147347300163600ustar00rootroot00000000000000name: "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 OpenSC-0.26.1/.github/ISSUE_TEMPLATE.md000066400000000000000000000015131474147347300170120ustar00rootroot00000000000000### Problem Description ### Proposed Resolution ### Steps to reproduce ### Logs OpenSC-0.26.1/.github/PULL_REQUEST_TEMPLATE.md000066400000000000000000000010531474147347300201050ustar00rootroot00000000000000 ##### 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 tokend is tested OpenSC-0.26.1/.github/actions/000077500000000000000000000000001474147347300157455ustar00rootroot00000000000000OpenSC-0.26.1/.github/actions/packit/000077500000000000000000000000001474147347300172205ustar00rootroot00000000000000OpenSC-0.26.1/.github/actions/packit/Dockerfile000066400000000000000000000002041474147347300212060ustar00rootroot00000000000000FROM quay.io/packit/packit RUN git config --system --add safe.directory /github/workspace ENTRYPOINT ["./.github/test-packit.sh"] OpenSC-0.26.1/.github/actions/packit/action.yml000066400000000000000000000001701474147347300212160ustar00rootroot00000000000000name: Packit RPM build description: Action that provides Packit RPM build runs: using: "docker" image: "Dockerfile" OpenSC-0.26.1/.github/add_signing_key.sh000077500000000000000000000024721474147347300177670ustar00rootroot00000000000000#!/bin/sh set -ex -o xtrace pushd .github/ tar xvf secrets.tar 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.cer \ -k ~/Library/Keychains/$KEY_CHAIN \ -T /usr/bin/codesign -T /usr/bin/productsign security import DeveloperIDInstaller.cer \ -k ~/Library/Keychains/$KEY_CHAIN \ -T /usr/bin/codesign -T /usr/bin/productsign security import key.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 OpenSC-0.26.1/.github/build.sh000077500000000000000000000052451474147347300157510ustar00rootroot00000000000000#!/bin/bash set -ex -o xtrace export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig; if [ -x "/bin/sudo" ]; then SUDO="sudo" fi if [ "$GITHUB_EVENT_NAME" == "pull_request" ]; then PR_NUMBER=$(echo $GITHUB_REF | awk 'BEGIN { FS = "/" } ; { print $3 }') if [ "$GITHUB_BASE_REF" == "master" ]; then SUFFIX="-pr$PR_NUMBER" else SUFFIX="$GITHUB_BASE_REF-pr$PR_NUMBER" fi fi if [ -n "$SUFFIX" ]; then ./bootstrap.ci -s "$SUFFIX" else ./bootstrap fi if [ "$RUNNER_OS" == "macOS" ]; then ./MacOSX/build exit $? 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 # normal procedure CONFIGURE_FLAGS="--disable-dependency-tracking --enable-doc" 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" 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 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 $SUDO make install if [ "$1" == "mingw" -o "$1" == "mingw32" ]; then # pack installed files wine "C:/Program Files/Inno Setup 5/ISCC.exe" win32/OpenSC.iss fi OpenSC-0.26.1/.github/cleanup-macos.sh000077500000000000000000000001551474147347300173740ustar00rootroot00000000000000#!/bin/bash set -ex -o xtrace if [ -n "$PASS_SECRETS_TAR_ENC" ]; then .github/remove_signing_key.sh fi OpenSC-0.26.1/.github/codespell_ignore_words.txt000066400000000000000000000002171474147347300216010ustar00rootroot00000000000000ans ba comandos componentes datas direccion ede fase feld gost hist ist keypair nmake oder parm parms requiere responde sie signatur standarts OpenSC-0.26.1/.github/dump-logs.sh000077500000000000000000000004671474147347300165620ustar00rootroot00000000000000SUFFIX=$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" cat $F echo "::endgroup::" done echo "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<" OpenSC-0.26.1/.github/opensc-piv.conf000066400000000000000000000003701474147347300172370ustar00rootroot00000000000000# Configuration for virtual PIV card to enforce mapping of ATR to type supporting EC app default { framework pkcs15 { use_file_caching = no; } card_atr 3b:80:80:01:01 { driver = "PIV-II"; type = "14001"; # SC_CARD_TYPE_PIV_II_GENERIC } } OpenSC-0.26.1/.github/push_artifacts.sh000077500000000000000000000024011474147347300176600ustar00rootroot00000000000000#!/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 OpenSC-0.26.1/.github/remove_signing_key.sh000077500000000000000000000002401474147347300205230ustar00rootroot00000000000000#!/bin/sh set -ex -o xtrace pushd .github/ security delete-keychain mac-build.keychain rm -f DeveloperIDApplication.cer DeveloperIDInstaller.cer key.p12 popd OpenSC-0.26.1/.github/restart-pcscd.sh000066400000000000000000000022561474147347300174240ustar00rootroot00000000000000#!/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 # 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 OpenSC-0.26.1/.github/secrets.tar.gpg000066400000000000000000000122331474147347300172420ustar00rootroot00000000000000  V>$wYwb`z#,ǹ-/OnD6P6w3A4f:W\yR}u#x}nfġI-z:qeLkJ|p +&Z{mYE*|䥫OmAJ}êtW?:eYZfy15Mwґ Orqr䨉ReFxq )hbDl{;.򜑯| EVǴBNI`Tx:;Z3: jbi\Z_ uH= `*mx_:nrY / ڒ]L!qD #6{g<0VҐD2WB6vq1FrBԙɩe/2kR']88ɼPH//)X(P*$c)n6ۤo+K8zv?w'$CCvuYSC5%1mZϕ 0U0ٟTf,)RJ͆(L)h ԓ}$L|v;[V,&lhRwD؍<0+>ٜ7t!e86mUsD 1 ̵_4goP8ekGo v1qR%nMu?}4 %$`f c#?6ibSlW6le|NF4P#)LXJ~i>j P f}WRHz _j. Ht,I<@ $%1 L8 7{F>>uOR|o^ffRiաjubkذW(V@GmnAÖN.ď|ܔ"Zt26tvv:%7o7okє^s=]|vu ;$Ϥ.H/JUN?B?6^]OfZHe6٨HrF7͐"RD$ ?StgMA&njzQWS}cտDV#O0ÿ0&Tpb%'-t^u)1 ON9f3yfw%:wBpJJc˻;a<"a" We]);Sz7Q䆷B 3߬R$ Ӄ]\[i@*c"q0\ Z&Z?W.AI3:|`w(sKH$6n9ט #cIWvWB'y&=K8f~! bt4c+݈(%:V[t9ŀ"O:k{iaff4Mn%g""EL ='𨋞fC7jRC yVmN$0A[y=XYx`xH +0*l҂TEzC;a8rqm ks9K6X6s}(T =Ah7s~*dC2MВ`(-*sN|-{ ]qMVc/E&,M~l j)XҍO5f6"{+IxtpuDx;+kR5ԃ~nl~jxIzΨ,U ;Gx=x?;ۘMm ?w1YfbT_ӾӰ`V؉%D*&y+EY!ywEVr8|v#5#6:k:bFuh:|r(0E<&ϋ|ʜWTLu;0zMAZA2Pv%oo`wb9>ned+)QTw̓ˡtK*Rg"t+՜ۦfɸ;ͺF/\>"th3MN /%D`A-vYDE$4u7\=~a0 q-t؎ʍxo+14gNX:i_[r M 2ݫy86I;Xwؽӡ`㈞TX޿rK#i- dtI7:&!>F_yM #}P^5`*i2')Q[,n^Y1"єm8"IO-oe~e Җk~RnKbI7wTAՌ)>@J68hcnze}ao#@3vYiS`ucn&HS#% Ս5!r ]mP<߭ct4 _EH ?^?˃d2ỴUFj%yX@asu;L_XĩX&?= `}əӽ .OGnyxh%.'#v4$oicaIioܘjŌ]FM l4İ\(NQʵ2payX)OUtg#%mӯPaC%2pwK8orb8iA§Cp~aYWDgib?9d}/mI/((7 8m#]n4RdB:lQrs68K_3 @$iT9rl}=m `r X_BF0peaB x@B>:`Nh52ho$c]Tx< ٬&q-*p+;BQgcP{3HƧ$.7i^:(!mfeҌSZ2fDN{1>Hs a, M4ot7ЧZ)!TAGy6"Y k@ MGOVOIږB)G|x@B#'uFθ kגYp/]sQx_Owe?B5O2@FA/l-8'g͂,B!Yv'yӰE\C$`CK'%dXf5i7򤐐R!.?3]&i|bau*4}K",TZo~3kHj0ѷД2cz4RխT>`U?-nӰ\% ۄ޿E@sB\&K)H~쯡 ;T%*9XzUA(P-oss2~6}X]ZZlT[تg)^>K0Cs0;ͼL(67x/g}ӿ'LvQJ;=h `oIIt^%o-(-2Hc;e^b.@yۈ\Y/ cʇ\fs^ە$ %%/DZ,Yr5qZ~HQ&$&JmCuc<DZL*T []C9ݪ1ܯe-m#35eJ{uFgL^fknQC]֙Ẃjl:y Y&c%0RpKx>o5ܤ?c\ ]5v:G Cr٣_؜,.]~qR:rI==1Vvuwr"nanj˺6ōwaE>Nzs8틕{sh~L^ZChtR,1D2Oih (b#$ch]rbJùywXbf~(ָCT`0&TxyusyQ5o$ۏ3C$<_"98QhOY땫@`md (څ|j/QփuAZIIZgD=rE(n7'-6tKk * )Nh%@/Fկ8| e6nFYa#޿㦼q'[u+3[[0 ګOpa֪*9)ICЩμmC \ {J1USN#v_ZչBӆS-3^pƣ>e|]^Bpm;uHf1܍GfPy[>-4:u}]L; ]J6)@NL; iWTYeeG*#0*uA>ao/ + j#z qZ{K`M%\QNSs4gXȝw(09QIf4I ?~m5'3&{qun+ԀYD=cY؇Z5=g>sgN؎OpenSC-0.26.1/.github/setup-fedora.sh000077500000000000000000000020571474147347300172460ustar00rootroot00000000000000#!/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" 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" else DEPS="$DEPS pcsc-lite-devel readline-devel openssl-devel zlib-ng-devel libcmocka-devel" 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 OpenSC-0.26.1/.github/setup-java.sh000077500000000000000000000017241474147347300167270ustar00rootroot00000000000000#!/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 OpenSC-0.26.1/.github/setup-libressl.sh000077500000000000000000000011061474147347300176170ustar00rootroot00000000000000#!/bin/bash set -ex -o xtrace # WARNING: Change this also in .github/workflows/linux.yml V=libressl-4.0.0 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 OpenSC-0.26.1/.github/setup-linux.sh000077500000000000000000000075741474147347300171560ustar00rootroot00000000000000#!/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" == "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" == "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 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 if [ -z "$GITHUB_ACTIONS" -o "$FORCE_INSTALL" = "1" ]; then # 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 fi # 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 elif [ "$1" == "debug" -o "$2" == "debug" ]; then # install debug symbols $SUDO apt-get install -y lsb-release ubuntu-dbgsym-keyring echo "deb http://ddebs.ubuntu.com $(lsb_release -cs) main restricted universe multiverse deb http://ddebs.ubuntu.com $(lsb_release -cs 2> /dev/null)-updates main restricted universe multiverse deb http://ddebs.ubuntu.com $(lsb_release -cs 2> /dev/null)-proposed main restricted universe multiverse" | \ $SUDO tee -a /etc/apt/sources.list.d/ddebs.list $SUDO apt-get update -qq DEP="libssl1.1-dbgsym" if [ -f "/usr/lib/x86_64-linux-gnu/libssl.so.3" ]; then DEP="libssl3t64-dbgsym" fi $SUDO apt-get install -y openssl-dbgsym "$DEP" softhsm2-dbgsym libsofthsm2-dbgsym 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 OpenSC-0.26.1/.github/setup-macos.sh000077500000000000000000000010201474147347300170750ustar00rootroot00000000000000#!/bin/bash set -ex -o xtrace brew install automake gengetopt help2man pkgconfig libtool # openSCToken export PATH="/usr/local/opt/ccache/libexec:$PATH" git clone https://github.com/frankmorgner/OpenSCToken.git sudo rm -rf /Library/Developer/CommandLineTools; if [ -n "$PASS_SECRETS_TAR_ENC" ]; then gpg --quiet --batch --yes --decrypt --passphrase="$PASS_SECRETS_TAR_ENC" --output .github/secrets.tar .github/secrets.tar.gpg .github/add_signing_key.sh; else unset CODE_SIGN_IDENTITY INSTALLER_SIGN_IDENTITY; fi OpenSC-0.26.1/.github/setup-valgrind.sh000066400000000000000000000014731474147347300176120ustar00rootroot00000000000000#!/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="/usr/lib/x86_64-linux-gnu/libpcsclite.so.1" fi OpenSC-0.26.1/.github/setup-vsmartcard.sh000077500000000000000000000004121474147347300201450ustar00rootroot00000000000000#!/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 OpenSC-0.26.1/.github/test-cac.sh000077500000000000000000000016621474147347300163540ustar00rootroot00000000000000#!/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 OpenSC-0.26.1/.github/test-gidsapplet.sh000077500000000000000000000026651474147347300177660ustar00rootroot00000000000000#!/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 OpenSC-0.26.1/.github/test-isoapplet.sh000077500000000000000000000101251474147347300176200ustar00rootroot00000000000000#!/bin/bash set -ex -o xtrace source .github/setup-valgrind.sh isoapplet_version="$1" if [ "$isoapplet_version" = "v0" ]; then isoapplet_branch="main-javacard-v2.2.2" elif [ "$isoapplet_version" = "v1" ]; then isoapplet_branch="main" else echo "Unknown IsoApplet version: $isoapplet_version" exit 1 fi 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 git clone https://github.com/philipWendland/IsoApplet.git --branch $isoapplet_branch --depth 1 # 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 OpenSC-0.26.1/.github/test-oseid.sh000077500000000000000000000040231474147347300167230ustar00rootroot00000000000000#!/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 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 OpenSC-0.26.1/.github/test-packit.sh000077500000000000000000000003371474147347300170770ustar00rootroot00000000000000#!/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 OpenSC-0.26.1/.github/test-piv.sh000077500000000000000000000056521474147347300164270ustar00rootroot00000000000000#!/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 # enforce the setting of different PIV type to support EC mechanisms # which are disabled for the generic ATR mapping to older Yubico devices export OPENSC_CONF="${PWD}/.github/opensc-piv.conf" $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 OpenSC-0.26.1/.github/workflows/000077500000000000000000000000001474147347300163425ustar00rootroot00000000000000OpenSC-0.26.1/.github/workflows/cifuzz.yml000066400000000000000000000013071474147347300204000ustar00rootroot00000000000000name: 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@v4 if: failure() && steps.build.outcome == 'success' with: name: artifacts path: ./out/artifacts OpenSC-0.26.1/.github/workflows/codeql.yml000066400000000000000000000017011474147347300203330ustar00rootroot00000000000000name: "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@v4 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} config-file: ./.github/.codeql.yml - run: .github/setup-linux.sh force-install - run: .github/build.sh - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v2 with: category: "/language:${{matrix.language}}" OpenSC-0.26.1/.github/workflows/codespell.yml000066400000000000000000000006611474147347300210420ustar00rootroot00000000000000--- 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@v4 - uses: codespell-project/actions-codespell@master with: skip: ./src/tests/fuzzing/corpus,compat_* ignore_words_file: .github/codespell_ignore_words.txt OpenSC-0.26.1/.github/workflows/coverity.yml000066400000000000000000000013741474147347300207360ustar00rootroot00000000000000name: 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@v4 - run: .github/setup-linux.sh force-install - run: ./bootstrap - run: ./configure --disable-dependency-tracking --enable-piv-sm - uses: vapier/coverity-scan-action@v0 if: ${{ env.HAS_COVERITY_SCAN_TOKEN == 'true' }} with: project: OpenSC%2FOpenSC token: ${{ secrets.COVERITY_SCAN_TOKEN }} email: 'viktor.tarasov@gmail.com' command: 'make' OpenSC-0.26.1/.github/workflows/doc.yml000066400000000000000000000023021474147347300176270ustar00rootroot00000000000000--- 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@v4 - 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 ) OpenSC-0.26.1/.github/workflows/fedora.yml000066400000000000000000000015751474147347300203350ustar00rootroot00000000000000name: 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@v4 - run: .github/setup-fedora.sh - run: .github/build.sh dist - name: Upload test logs uses: actions/upload-artifact@v4 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@v4 - run: .github/setup-fedora.sh ix86 - run: .github/build.sh ix86 OpenSC-0.26.1/.github/workflows/formatter.yml000066400000000000000000000005221474147347300210670ustar00rootroot00000000000000name: Check Code Style on: pull_request: paths: - '**.c' - '**.h' - .github/workflows/formatter.yml jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 100 - uses: yshui/git-clang-format-lint@master #with: # base: origin/master OpenSC-0.26.1/.github/workflows/linux-strict.yml000066400000000000000000000055541474147347300215430ustar00rootroot00000000000000--- name: Linux (latest, strict) on: pull_request: paths: - '**.c' - '**.h' - '**.sh' - .github/workflows/linux.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 # -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 -Wno-strict-prototypes -Wno-empty-translation-unit -Wno-incompatible-library-redeclaration -Wno-variadic-macros -Wno-unused-function -Wno-incompatible-pointer-types-discards-qualifiers -Wno-gnu-zero-variadic-macro-arguments -Wno-format-pedantic -Wno-pedantic -Wno-incompatible-function-pointer-types 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@v4 - run: .github/setup-linux.sh ${{ matrix.compiler }} force-install - run: .github/build.sh dist env: CC: ${{ matrix.compiler }} CFLAGS: ${{ env.CFLAGS }} ${{ matrix.compiler == 'clang' && env.CLANG_CFLAGS || '' }} - name: Upload test logs uses: actions/upload-artifact@v4 if: failure() with: name: ${{ matrix.name }}-${{ matrix.compiler }}-strict-test-logs path: | config.log tests/*.log src/tests/unittests/*.log OpenSC-0.26.1/.github/workflows/linux.yml000066400000000000000000000427771474147347300202450ustar00rootroot00000000000000--- 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 LIBRESSL_VERSION: 4.0.0 jobs: build: runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v4 - uses: awalsh128/cache-apt-pkgs-action@latest with: packages: ${{ env.BASE_DEPS }} version: apt-20-base execute_install_scripts: true - run: .github/setup-linux.sh - run: .github/build.sh dist - name: Upload test logs uses: actions/upload-artifact@v4 if: failure() with: name: ubuntu-test-logs path: | tests/*.log src/tests/unittests/*.log - uses: actions/cache@v4 id: cache-build with: path: ./* key: ${{ runner.os }}-${{ github.sha }} - name: Upload build artifacts uses: actions/upload-artifact@v4 with: name: opensc-build path: opensc*.tar.gz valgrind: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: awalsh128/cache-apt-pkgs-action@latest with: packages: ${{ env.BASE_DEPS }} version: apt-latest-base execute_install_scripts: true - run: .github/setup-linux.sh debug - run: .github/build.sh valgrind - name: Upload test logs uses: actions/upload-artifact@v4 if: failure() with: name: ubuntu-valgrind-logs path: | tests/*.log src/tests/unittests/*.log build-no-shared: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: awalsh128/cache-apt-pkgs-action@latest 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-openssl: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: awalsh128/cache-apt-pkgs-action@latest 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-20.04 steps: - uses: actions/checkout@v4 - uses: awalsh128/cache-apt-pkgs-action@latest with: packages: ${{ env.BASE_DEPS }} version: apt-20-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@v4 - run: .github/setup-linux.sh mingw force-install - run: .github/build.sh mingw - name: Cache build artifacts uses: actions/upload-artifact@v4 with: name: opensc-build-mingw path: win32/Output/OpenSC*.exe build-mingw32: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: awalsh128/cache-apt-pkgs-action@latest with: packages: ${{ env.BASE_DEPS }} version: apt-latest-base execute_install_scripts: true - run: .github/setup-linux.sh mingw32 - run: .github/build.sh mingw32 - name: Cache build artifacts uses: actions/upload-artifact@v4 with: name: opensc-build-mingw32 path: win32/Output/OpenSC*.exe build-piv-sm: runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v4 - uses: awalsh128/cache-apt-pkgs-action@latest with: packages: ${{ env.BASE_DEPS }} ${{ env.JAVA_DEPS }} version: apt-20-java execute_install_scripts: true - run: .github/setup-linux.sh - run: .github/build.sh piv-sm dist - name: Upload test logs uses: actions/upload-artifact@v4 if: failure() with: name: ubuntu-piv-sm-test-logs path: | tests/*.log src/tests/unittests/*.log - uses: actions/cache@v4 id: cache-build with: path: ./* key: ${{ runner.os }}-piv-sm-${{ github.sha }} test-piv: runs-on: ubuntu-20.04 needs: [build] steps: - uses: actions/checkout@v4 - uses: awalsh128/cache-apt-pkgs-action@latest with: packages: ${{ env.BASE_DEPS }} ${{ env.JAVA_DEPS }} version: apt-20-java execute_install_scripts: true - uses: actions/cache@v4 id: cache-build with: path: ./* key: ${{ runner.os }}-${{ github.sha }} - run: git clone $JCARDSIM - uses: actions/setup-java@v4 with: distribution: 'semeru' java-version: '8' cache: 'maven' - run: .github/setup-linux.sh piv - run: .github/test-piv.sh - run: .github/test-piv.sh valgrind test-piv-sm: runs-on: ubuntu-20.04 needs: [build-piv-sm] steps: - uses: actions/checkout@v4 - uses: awalsh128/cache-apt-pkgs-action@latest with: packages: ${{ env.BASE_DEPS }} ${{ env.JAVA_DEPS }} version: apt-20-java execute_install_scripts: true - uses: actions/cache@v4 id: cache-build with: path: ./* key: ${{ runner.os }}-piv-sm-${{ github.sha }} - run: git clone $JCARDSIM - uses: actions/setup-java@v4 with: distribution: 'semeru' java-version: '8' cache: 'maven' - run: .github/setup-linux.sh piv - run: .github/test-piv.sh - run: .github/test-piv.sh valgrind test-isoapplet-v0: runs-on: ubuntu-20.04 needs: [build] steps: - uses: actions/checkout@v4 - uses: awalsh128/cache-apt-pkgs-action@latest with: packages: ${{ env.BASE_DEPS }} ${{ env.JAVA_DEPS }} version: apt-20-java execute_install_scripts: true - run: git clone $JCARDSIM - uses: actions/setup-java@v4 with: distribution: 'semeru' java-version: '8' cache: 'maven' - uses: actions/cache@v4 id: cache-build with: path: ./* key: ${{ runner.os }}-${{ github.sha }} - run: .github/setup-linux.sh isoapplet - run: .github/test-isoapplet.sh v0 - run: .github/test-isoapplet.sh v0 valgrind test-isoapplet-v1: runs-on: ubuntu-20.04 needs: [build] steps: - uses: actions/checkout@v4 - uses: awalsh128/cache-apt-pkgs-action@latest with: packages: ${{ env.BASE_DEPS }} ${{ env.JAVA_DEPS }} version: apt-20-java execute_install_scripts: true - uses: actions/cache@v4 id: cache-build with: path: ./* key: ${{ runner.os }}-${{ github.sha }} - run: git clone $JCARDSIM - uses: actions/setup-java@v4 with: distribution: 'semeru' java-version: '8' cache: 'maven' - run: .github/setup-linux.sh isoapplet - run: .github/test-isoapplet.sh v1 - run: .github/test-isoapplet.sh v1 valgrind test-gidsapplet: runs-on: ubuntu-20.04 needs: [build] steps: - uses: actions/checkout@v4 - uses: awalsh128/cache-apt-pkgs-action@latest with: packages: ${{ env.BASE_DEPS }} ${{ env.JAVA_DEPS }} version: apt-20-java execute_install_scripts: true - uses: actions/cache@v4 id: cache-build with: path: ./* key: ${{ runner.os }}-${{ github.sha }} - run: git clone $JCARDSIM - uses: actions/setup-java@v4 with: distribution: 'semeru' java-version: '8' cache: 'maven' - run: .github/setup-linux.sh gidsapplet - run: .github/test-gidsapplet.sh - run: .github/test-gidsapplet.sh valgrind test-openpgp: runs-on: ubuntu-20.04 needs: [build] steps: - uses: actions/checkout@v4 - uses: awalsh128/cache-apt-pkgs-action@latest with: packages: ${{ env.BASE_DEPS }} %{{ env.JAVA_DEPS }} version: apt-20-base execute_install_scripts: true - uses: actions/cache@v4 id: cache-build with: path: ./* key: ${{ runner.os }}-${{ github.sha }} - run: git clone $JCARDSIM - uses: actions/setup-java@v4 with: distribution: 'semeru' java-version: '8' cache: 'maven' - run: .github/setup-linux.sh openpgp - run: .github/test-openpgp.sh - run: .github/test-openpgp.sh valgrind build-clang-tidy: runs-on: ubuntu-20.04 needs: [build] steps: - uses: actions/checkout@v4 - uses: awalsh128/cache-apt-pkgs-action@latest with: packages: ${{ env.BASE_DEPS }} version: apt-20-base execute_install_scripts: true - uses: actions/cache@v4 id: cache-build with: path: ./* key: ${{ runner.os }}-${{ github.sha }} - run: .github/setup-linux.sh clang-tidy - run: .github/build.sh clang-tidy test-cac: runs-on: ubuntu-20.04 needs: [build] steps: - uses: actions/checkout@v4 - uses: awalsh128/cache-apt-pkgs-action@latest with: packages: ${{ env.BASE_DEPS }} version: apt-20-base execute_install_scripts: true - uses: actions/cache@v4 id: cache-build with: path: ./* key: ${{ runner.os }}-${{ github.sha }} - run: .github/setup-linux.sh cac - run: .github/test-cac.sh - run: .github/test-cac.sh valgrind test-oseid: runs-on: ubuntu-20.04 needs: [build] steps: - uses: actions/checkout@v4 - uses: awalsh128/cache-apt-pkgs-action@latest with: packages: ${{ env.BASE_DEPS }} version: apt-20-base execute_install_scripts: true - uses: actions/cache@v4 id: cache-build with: path: ./* key: ${{ runner.os }}-${{ github.sha }} - run: .github/setup-linux.sh oseid - run: .github/test-oseid.sh - run: .github/test-oseid.sh valgrind ############################################ ## Ubuntu 22.04 with OpenSSL 3.0 pipeline ## ############################################ build-ubuntu-22: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 - uses: awalsh128/cache-apt-pkgs-action@latest with: packages: ${{ env.BASE_DEPS }} version: apt-22-base execute_install_scripts: true - run: .github/setup-linux.sh - run: .github/build.sh dist valgrind - uses: actions/upload-artifact@v4 if: failure() with: name: ubuntu-22-test-logs path: | tests/*.log src/tests/unittests/*.log - uses: actions/cache@v4 id: cache-build if: ${{ success() }} with: path: ./* key: ${{ runner.os }}-22-${{ github.sha }} - name: Upload artifacts uses: actions/upload-artifact@v4 if: ${{ success() }} with: name: opensc-22-build path: opensc*.tar.gz build-ubuntu-22-piv-sm: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 - uses: awalsh128/cache-apt-pkgs-action@latest with: packages: ${{ env.BASE_DEPS }} ${{ env.JAVA_DEPS }} version: apt-22-java execute_install_scripts: true - run: .github/setup-linux.sh - run: .github/build.sh piv-sm dist valgrind - uses: actions/upload-artifact@v4 if: failure() with: name: ubuntu-22-piv-sm-test-logs path: | tests/*.log src/tests/unittests/*.log - uses: actions/cache@v4 id: cache-build if: ${{ success() }} with: path: ./* key: ${{ runner.os }}-22-piv-sm-${{ github.sha }} test-piv-sm-ubuntu-22: runs-on: ubuntu-22.04 needs: [build-ubuntu-22-piv-sm] steps: - uses: actions/checkout@v4 - uses: awalsh128/cache-apt-pkgs-action@latest with: packages: ${{ env.BASE_DEPS }} ${{ env.JAVA_DEPS }} version: apt-22-java execute_install_scripts: true - uses: actions/cache@v4 id: cache-build with: path: ./* key: ${{ runner.os }}-22-piv-sm-${{ github.sha }} - run: git clone $JCARDSIM - uses: actions/setup-java@v4 with: distribution: 'semeru' java-version: '8' cache: 'maven' - run: .github/setup-linux.sh piv - run: .github/test-piv.sh - run: .github/test-piv.sh valgrind test-cac-ubuntu-22: runs-on: ubuntu-22.04 needs: [build-ubuntu-22] steps: - uses: actions/checkout@v4 - uses: awalsh128/cache-apt-pkgs-action@latest with: packages: ${{ env.BASE_DEPS }} ${{ env.JAVA_DEPS }} version: apt-22-java execute_install_scripts: true - uses: actions/cache@v4 id: cache-build with: path: ./* key: ${{ runner.os }}-22-${{ github.sha }} - run: .github/setup-linux.sh cac - run: .github/test-cac.sh - run: .github/test-cac.sh valgrind test-oseid-ubuntu-22: runs-on: ubuntu-22.04 needs: [build-ubuntu-22] steps: - uses: actions/checkout@v4 - uses: awalsh128/cache-apt-pkgs-action@latest with: packages: ${{ env.BASE_DEPS }} version: apt-22-base execute_install_scripts: true - uses: actions/cache@v4 id: cache-build with: path: ./* key: ${{ runner.os }}-22-${{ github.sha }} - run: .github/setup-linux.sh oseid - run: .github/test-oseid.sh - run: .github/test-oseid.sh valgrind ####################### ## LibreSSL pipeline ## ####################### build-libressl: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: awalsh128/cache-apt-pkgs-action@latest with: packages: ${{ env.BASE_DEPS }} version: apt-latest-base execute_install_scripts: true - name: Restore LibreSSL build uses: actions/cache/restore@v4 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@v4 if: failure() with: name: libressl-logs path: | config.log tests/*.log src/tests/unittests/*.log - uses: actions/cache@v4 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@v4 with: path: | libressl-${{ env.LIBRESSL_VERSION }}/ key: libressl-${{ env.LIBRESSL_VERSION }} test-cac-libressl: runs-on: ubuntu-latest needs: [build-libressl] steps: - uses: actions/checkout@v4 - uses: awalsh128/cache-apt-pkgs-action@latest with: packages: ${{ env.BASE_DEPS }} version: apt-latest-base execute_install_scripts: true - uses: actions/cache@v4 id: cache-build with: path: ./* key: ${{ runner.os }}-libressl-${{ github.sha }} - run: .github/setup-linux.sh cac libressl - run: .github/test-cac.sh - run: .github/test-cac.sh valgrind test-oseid-libressl: runs-on: ubuntu-latest needs: [build-libressl] steps: - uses: actions/checkout@v4 - uses: awalsh128/cache-apt-pkgs-action@latest with: packages: ${{ env.BASE_DEPS }} version: apt-latest-base execute_install_scripts: true - uses: actions/cache@v4 id: cache-build with: path: ./* key: ${{ runner.os }}-libressl-${{ github.sha }} - run: .github/setup-linux.sh oseid libressl - run: .github/test-oseid.sh - run: .github/test-oseid.sh valgrind #################### ## Push artifacts ## #################### push-artifacts: runs-on: ubuntu-latest needs: [build, build-mingw] steps: - uses: actions/checkout@v4 - uses: actions/cache@v4 id: cache-build with: path: ./* key: ${{ runner.os }}-${{ github.sha }} - name: Pull mingw build artifacts uses: actions/download-artifact@v4 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' }} OpenSC-0.26.1/.github/workflows/macos.yml000066400000000000000000000033111474147347300201650ustar00rootroot00000000000000name: 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-15, macos-14, macos-13] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - run: .github/setup-macos.sh env: KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }} PASS_SECRETS_TAR_ENC: ${{ secrets.PASS_SECRETS_TAR_ENC }} - run: .github/build.sh env: CODE_SIGN_IDENTITY: ${{ secrets.CODE_SIGN_IDENTITY }} DEVELOPMENT_TEAM: ${{ secrets.DEVELOPMENT_TEAM }} INSTALLER_SIGN_IDENTITY: ${{ secrets.INSTALLER_SIGN_IDENTITY }} - name: Cache build artifacts uses: actions/upload-artifact@v4 with: name: opensc-build-${{ matrix.os }} path: OpenSC*.dmg - run: .github/cleanup-macos.sh env: KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }} PASS_SECRETS_TAR_ENC: ${{ secrets.PASS_SECRETS_TAR_ENC }} push-artifacts: runs-on: macos-latest needs: [build] steps: - uses: actions/checkout@v4 - name: Pull build artifacts uses: actions/download-artifact@v4 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' }} OpenSC-0.26.1/.github/workflows/packit.yaml000066400000000000000000000017141474147347300205040ustar00rootroot00000000000000name: Packit on: pull_request: paths: - '**.c' - '**.h' - '**.am' - '**.sh' - .github/workflows/packit.yaml - packaging/opensc.spec - configure.ac push: jobs: packit_srpm: runs-on: ubuntu-latest name: Packit SRPM steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: packit/actions/srpm@main - name: Upload build artifacts uses: actions/upload-artifact@v4 with: name: opensc-srpm path: opensc*.src.rpm packit_rpm: runs-on: ubuntu-latest name: Packit RPM steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: ./.github/actions/packit - name: Upload build artifacts uses: actions/upload-artifact@v4 with: name: opensc-rpm path: x86_64/opensc*.rpm OpenSC-0.26.1/.gitignore000066400000000000000000000050141474147347300147350ustar00rootroot00000000000000Makefile Makefile.in core archive acinclude.m4 aclocal.m4 aminclude_static.am autom4te.cache compile confdefs.h config.* configure conftest conftest.c depcomp install-sh libtool libtool.m4 lt*.m4 ltmain.sh missing mkinstalldirs so_locations stamp-h* tags test-driver .deps .libs .dirstamp .#*# .*.bak .*.orig .*.rej .*~ #*# *.bak *.d *.def *.dll *.exe *.la *.lib *.lo *.orig *.pdb *.rej *.u *.rc *.pc *~ *.o *.gz *.bz2 *.[0-9] *.gif *.css *.out *.tmp *.obj *.exp *.res *.ggo ChangeLog .cache compile_commands.json .dirstamp doc/tools/*-tool doc/tools/eidenv doc/tools/opensc-explorer doc/tools/pkcs11-register doc/tools/pkcs15-crypt doc/tools/pkcs15-init doc/tools/opensc-asn1 doc/tools/opensc-notify doc/files/opensc.conf.5.xml doc/files/pkcs15-profile.5.xml etc/opensc.conf.example src/common/compat_getopt_main src/minidriver/opensc-minidriver.inf src/tools/*-tool src/tools/pkcs15-crypt src/tools/pkcs15-init src/tools/eidenv src/tools/opensc-explorer src/tools/cardos-info 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/resources/Welcome.html *.dmg *.pkg OpenSC.tokend/ build/ engine_pkcs11/ libp11/ target/ src/scconf/test-conf src/tests/base64 src/tests/lottery src/tests/p15dump src/tests/pintest src/tests/prngtest src/tests/p11test/p11test 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_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 version.m4.ci openssl_arm64 openssl_bin OpenSC-0.26.1/.ignoreRevsFile000066400000000000000000000022671474147347300157000ustar00rootroot0000000000000070771735ae10180bb039043b9a1b00b66bf00fc1 # 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 OpenSC-0.26.1/.packit.yaml000066400000000000000000000007611474147347300151660ustar00rootroot00000000000000upstream_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 OpenSC-0.26.1/CONTRIBUTING.md000066400000000000000000000037161474147347300152050ustar00rootroot00000000000000# Formatting style 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 OpenSC-0.26.1/COPYING000066400000000000000000000636321474147347300140120ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! OpenSC-0.26.1/MacOSX/000077500000000000000000000000001474147347300140375ustar00rootroot00000000000000OpenSC-0.26.1/MacOSX/Distribution.xml.in000066400000000000000000000037501474147347300176520ustar00rootroot00000000000000 @PACKAGE_STRING@ OpenSC.pkg OpenSC-tokend.pkg OpenSCToken.pkg OpenSC-startup.pkg OpenSC-0.26.1/MacOSX/Distribution_universal.xml.in000066400000000000000000000040111474147347300217310ustar00rootroot00000000000000 @PACKAGE_STRING@ OpenSC.pkg OpenSC-tokend.pkg OpenSCToken.pkg OpenSC-startup.pkg OpenSC-0.26.1/MacOSX/Makefile.am000066400000000000000000000004741474147347300161000ustar00rootroot00000000000000MAINTAINERCLEANFILES = $(srcdir)/Makefile.in EXTRA_DIST = build \ build-package.in \ Distribution.xml.in \ Distribution_universal.xml.in \ libtool-bundle \ opensc-uninstall \ resources \ resources/background.jpg \ resources/Welcome.html.in \ scripts \ scripts/postinstall \ OpenSC_Uninstaller.applescript OpenSC-0.26.1/MacOSX/OpenSC_Notify.applescript000066400000000000000000000001751474147347300207710ustar00rootroot00000000000000do shell script "killall opensc-notify || true" do shell script "nohup /Library/OpenSC/bin/opensc-notify > /dev/null 2>&1 &" OpenSC-0.26.1/MacOSX/OpenSC_Uninstaller.applescript000066400000000000000000000006251474147347300220210ustar00rootroot00000000000000tell application "System Events" if exists file "/usr/local/bin/opensc-uninstall" then set result to do shell script "/usr/local/bin/opensc-uninstall" with administrator privileges display alert "Removal complete" message result giving up after 10 else display alert "OpenSC is not installed" message "Could not find /usr/local/bin/opensc-uninstall" as critical giving up after 10 end if end tellOpenSC-0.26.1/MacOSX/OpenSC_applescripts.entitlements000066400000000000000000000005011474147347300224100ustar00rootroot00000000000000 com.apple.security.app-sandbox com.apple.security.automation.apple-events OpenSC-0.26.1/MacOSX/OpenSC_binaries.entitlements000066400000000000000000000005071474147347300215010ustar00rootroot00000000000000 com.apple.security.app-sandbox com.apple.security.cs.disable-library-validation OpenSC-0.26.1/MacOSX/build000077500000000000000000000003111474147347300150570ustar00rootroot00000000000000#!/bin/bash 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 $@ OpenSC-0.26.1/MacOSX/build-package.in000077500000000000000000000271031474147347300170650ustar00rootroot00000000000000#!/bin/bash # Build the macOS installer for the tokend 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 test -x ./configure || ./bootstrap BUILDPATH=${PWD} xcode_ver=$(xcodebuild -version | sed -En 's/Xcode[[:space:]](.*)/\1/p') base_ver="12.2" if [ $(echo -e $base_ver"\n"$xcode_ver | sort -V | head -1) == "$base_ver" ]; then export BUILD_ARM="true" fi export SED=/usr/bin/sed 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 if ! test -e openssl; then git clone --depth=1 https://github.com/openssl/openssl.git -b openssl-3.0 fi cd openssl ./Configure darwin64-x86_64 no-shared --prefix=$PREFIX enable-ec_nistp_64_gcc_128 make clean make -j 4 make DESTDIR=$BUILDPATH/openssl_bin install_sw if test -n "${BUILD_ARM}"; then make clean ./Configure darwin64-arm64 no-shared --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 fi 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`" export CRYPTO_CFLAGS="$OPENSSL_CFLAGS" export CRYPTO_LIBS="$OPENSSL_LIBS" fi # Locate the latest OSX SDK SDK_PATH=$(xcrun --sdk macosx --show-sdk-path) export CFLAGS="$CFLAGS -isysroot $SDK_PATH" if test -n "${BUILD_ARM}"; then export CFLAGS="$CFLAGS -arch x86_64 -arch arm64" export LDFLAGS="$LDFLAGS -arch x86_64 -arch arm64" DISTRIBUTION_XML=MacOSX/Distribution_universal.xml else DISTRIBUTION_XML=MacOSX/Distribution.xml fi 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.3 fi cd openpace autoreconf -vis ./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 export OPENPACE_CFLAGS="`env PKG_CONFIG_PATH=$BUILDPATH/openssl_bin/$PREFIX/lib/pkgconfig:$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/openssl_bin/$PREFIX/lib/pkgconfig:$BUILDPATH/openpace_bin/$PREFIX/lib/pkgconfig PKG_CONFIG_SYSROOT_DIR=$BUILDPATH/openpace_bin 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 \ --enable-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 rm -f ${BUILDPATH}/target/$PREFIX/lib/*.a # 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/ \ 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/ 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 # Build OpenSC.tokend when XCode version < 10 if (( $(xcodebuild -version | sed -En 's/Xcode[[:space:]]+([0-9]+)(\.[0-9]*)*/\1/p') < 10 )); then # Check out OpenSC.tokend, if not already fetched. if ! test -e OpenSC.tokend; then git clone https://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 if test -n "${CODE_SIGN_IDENTITY}" -a -n "${DEVELOPMENT_TEAM}"; then xcodebuild -target OpenSC -configuration Deployment -project OpenSC.tokend/Tokend.xcodeproj install DSTROOT=${BUILDPATH}/target_tokend \ 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 OpenSC -configuration Deployment -project OpenSC.tokend/Tokend.xcodeproj install DSTROOT=${BUILDPATH}/target_tokend fi TOKEND="-tokend" else # https://github.com/OpenSC/OpenSC.tokend/issues/33 mkdir -p ${BUILDPATH}/target_tokend TOKEND="" fi #if ! test -e $BUILDPATH/target/Library/Security/tokend/OpenSC.tokend/Contents/Resources/Applications/terminal-notifier.app; then #if ! test -e terminal-notifier-1.7.1.zip; then #curl -L https://github.com/julienXX/terminal-notifier/releases/download/1.7.1/terminal-notifier-1.7.1.zip > terminal-notifier-1.7.1.zip #fi #if ! test -e terminal-notifier-1.7.1; then #unzip terminal-notifier-1.7.1.zip #fi #mkdir -p $BUILDPATH/target/Library/Security/tokend/OpenSC.tokend/Contents/Resources/Applications #cp -r terminal-notifier-1.7.1/terminal-notifier.app $BUILDPATH/target/Library/Security/tokend/OpenSC.tokend/Contents/Resources/Applications #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_tokend --component-plist MacOSX/target_tokend.plist --identifier org.opensc-project.tokend --version @PACKAGE_VERSION@ --install-location / OpenSC-tokend.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 $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@$TOKEND.dmg i=0 while ! hdiutil create -srcfolder "${imagedir}" -volname "@PACKAGE_NAME@" -fs JHFS+ OpenSC-@PACKAGE_VERSION@$TOKEND.dmg do i=$[$i+1] if [ $i -gt 2 ] then exit 1 fi done rm -rf ${imagedir} #if [ "$TRAVIS_EVENT_TYPE" != "pull_request" ]; then xcrun altool --notarize-app --file $(pwd)/vorteil_darwin-x86.dmg --username $OSX_NOTARIZE_USERNAME --primary-bundle-id com.vorteil.cli -p $OSX_NOTARIZE_PW -- >> /dev/null; fi; #if [ "$TRAVIS_EVENT_TYPE" != "pull_request" ]; then for ((i=1;i<=30;i+=1)); do xcrun stapler staple $(pwd)/vorteil_darwin-x86.dmg >> /dev/null; if [ $? = 65 ]; then echo "Waiting for notarization to complete..." && sleep 10; fi; done; fi; OpenSC-0.26.1/MacOSX/libtool-bundle000077500000000000000000000045321474147347300167040ustar00rootroot00000000000000#!/bin/sh # A shell script to create MacOS X bundles # from files created by GNU libtool. # Incomplete, but works. # # $Id: libtool-bundle 1533 2003-10-16 20:41:34Z aet $ # # set -e verbose=0 verbose_msg () { if [ $verbose -ne 0 ]; then echo "libtool-bundle: $@" fi } error_msg () { echo 1>&2 "libtool-bundle: $@" } usage () { error_msg "Usage: $0 [-e extra XML data] [Mach-O bundle file] [destination directory] " exit 1 } case $1 in -e) shift; if [ "$1" ]; then extradata=$1; shift; else usage; fi; ;; esac [ $# -le 1 -o $# -ge 4 ] && usage sofile=$1 [ ! -f $sofile ] && error_msg "Not a file or file not found: $sofile" && exit 1 case "$sofile" in *.so*) # Assume it's ok ;; *) error_msg "Invalid bundle: $sofile" exit 1 ;; esac destdir=$2 [ ! -d $destdir -o ! -w $destdir ] && error_msg "Not a directory or no write access: $destdir" && exit 1 name="$sofile" [ $# -eq 3 ] && name=$3 name=`echo $name | sed -e "s@.*/@@" -e "s@\.so.*@@"` root="$destdir/${name}.bundle" verbose_msg "sofile: $sofile" verbose_msg "destdir: $destdir" verbose_msg "name: $name" verbose_msg "root: $root" arch=`uname` [ x$arch = xDarwin ] && arch=MacOS type="BNDL" creator="????" # Overwrite existing bundle [ -d "$root" ] && rm -rf "$root" mkdir -p "$root"/Contents/$arch cp "$sofile" "$root"/Contents/$arch/"$name" echo "$type$creator" > "$root"/Contents/PkgInfo create_info_plist () { echo "" echo "" echo "" echo "" echo " CFBundleDevelopmentRegion" echo " English" echo " CFBundleExecutable" echo " $name" echo " CFBundleInfoDictionaryVersion" echo " 6.0" echo " CFBundleName" echo " $name" echo " CFBundlePackageType" echo " $type" echo " CFBundleSignature" echo " $creator" echo " CFBundleVersion" echo " 0.0.1d1" if [ "$extradata" ]; then echo "" [ -f "$extradata" ]; cat $extradata fi echo "" echo "" } create_info_plist > "$root"/Contents/Info.plist echo "Installed $sofile as $root" OpenSC-0.26.1/MacOSX/opensc-uninstall000077500000000000000000000033521474147347300172660ustar00rootroot00000000000000#!/bin/bash if [ "$(id -u)" != "0" ]; then echo "This script must be run as root:" 1>&2 echo "" 1>&2 echo "sudo /usr/local/bin/opensc-uninstall" 1>&2 exit 1 fi 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 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 # 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!" OpenSC-0.26.1/MacOSX/resources/000077500000000000000000000000001474147347300160515ustar00rootroot00000000000000OpenSC-0.26.1/MacOSX/resources/Welcome.html.in000066400000000000000000000027101474147347300207370ustar00rootroot00000000000000 Welcome to OpenSC

@OPENSC_VS_FF_PRODUCT_NAME@

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.

@OPENSC_VS_FF_COMMENTS@

Documentation

The OpenSC Wiki is available at: https://github.com/OpenSC/OpenSC/wiki and should be consulted for further documentation and support.

OpenSC-0.26.1/MacOSX/resources/background.jpg000066400000000000000000000315051474147347300206760ustar00rootroot00000000000000JFIF,,       l AQ!1a"2qBr3R#b6Ѳst7c$4!1Aa"3q2Q4RrѲD ??: DU49zbnU:wi|q8gyc 'R IȪUN4A9>96dP5κ P)zbjVe#um|q8ee,M21,M_qթUʻ|Tٶz}z}-99dЧ6T(@U:36qh>XXw_4ZJ)2Syه>7Yd9-k1PT @PTnR+./;dwUˉ,tc,WeT#Z1v׳m4Zs62ɤ @\ ";*vKwc5xğ7{gcIդ+u;qCsuKH܎cֹκP@P*zbYVV]_:wV_NK_r:ΪUN4{i<&@  Q4Zp5#R3u}>gN 9yr`r&mZDQ46槊&x5l'?7^YXui(]NfPdR,m7#䵮@>9TU@@UXmoUeYX5DOiJ\q.ͪpsA@DP5zjFe|Νr;Lڸ ECmjxkRBҶXfz{]/w,Ӭ}i:Reur(}Nn)c6rZ @~u@T(E(Pbޫʲt}>GNrp EPU TM5zo2}>gN6C^gu\rɛV )@ &\Cr+$K<4ZN%]kܼÊSX卲Fsκ@*Bbޫʲ}>GN]rp q@ *Eg+w޸e|νl 0862 @PT ↥Y^ }l'?7^YXui(]NfPdR,m7#䵮@>g?: P TTfoUeYY>#um|ܮq8ee T*QZJ)2Syه>7Yd9-k   @rśW%ee|ղ r:1+0O=ztd|νl 08喬e PPQ gəl{q_,mw&K<4Iդ+u;qCsuKH܎cֹg?8P(Q  TRśLK*͵OӺA>nWu\rF2qTO>[Zztœ:m2;ZK0 (Q fVdgDW/UQĜ )( 37?z|}9׼pUz'_ćìk棚RT 3@ P RśLK*пt RO/:?Wt쭰AW& ,dU@@ &ZSR5#:"|ڴDzZ"06F:%䵫v񺭚8\]b=yu|΋u$?AGg?8PU @QJATTfoUeYXףoٵmE,6Ry\֧4@PU (QDYkZ*jFgDW/VF%bԵDvoBŎwT%Znz#N=^6Z8N)r*n_0QRܻ2^Kі:BU@( Xs7IJOӺ>~Wu\3G#um|ܮq8ee T*g{-kWMR3"+5EDzZȀwX/RŎwS=Znb#N=^6:a^edM{Va15DO@3} gVYf\j,dw#bnt[M:U=E5rrlըU@T(Pb%ef#ul|ܮq8厌e (yֵ|T-̈_$:4HͫbԵD U-w5,jb-G6UrU[Uxɣ{=DT-S-(@>YQbuFEUrݗӲU†.IjrzbjU_OQg+XG6@ Knf1,+7GtOW.'ь\ay5|T-2"|TQ#1k6 2JjE5~WxozZvs;z܉z0iƜMުU۲V-^aıӊnSR貱kf.FܸP TP Knf^U:wUR-êxDW/VF%sVRg+w޸dxz~{ayw==>ƟUs?HMM5D͊&{TvCVJ&+lyAOOփ (FN7)eYX5˳mVA=5WXF@U:62n51WXde|FآF%ԿڰPRg+w޸dxza^]z_Cc\1SMQ3b>']ÕһI ݀ (P#Gܥed{.ͷ'IZ]""-ˁKl  TLYkZoSR,ʛ<]Qjy՛V T2g+w޸dxza^]z_Cӭs?HMM5D͆&{TvCVJ&<e?<@P$hred{.ͷ&itٱWXG6P@ fZzdPTqBKWkw-Ya 3Mg+w޸E{ayw==?O'']ÕһI~y (P fVp,#7Wt-I9]oLMʫıWX@ i+Fv*H歭^&Ӣ 1-]_,bՆP( 5zjFxZa^Mz_8Z~jM|89:7MrLx'6 睐)k9[ԍH;+\0wUɁ,i|K=85.+vm1:J'bܸ!T3MejMH(DͲQw&&*7%{p.0O]+Yb͍&A@LYYgWsޗNֱ~j M|89:7MrLx'6; AB fZjF^Z/3LYdͫ|I=8ܥecs.ͷ'IZ[ %rg,RƓ(LYkZoSR,ej$f1jZK=-N)+"WfZ`s\>Wś 2izkZj{ay7=z\~>k驦wÊcthai]$ǂp`1d(DYH{4yr`q&mZF@ (P#Gcٶ+Kabܸ,i23MejMH(DW/ ](Wũk,H:*ȨUٲ:wV'.ϕg^6A^gu\rV2q@T(BU;ڻzoSR5~jM|89:7MrLx'6XjǍ5ANgu\2W<\a@@PU (Pֵ|TԍHΈ_$6QDڽ^F-KV)Rg+w޸dxza^Mz_8\1Q3b>']ÕһIfFOΏ-EKSj3PU@T((TQ4Z֯ժ(jyfՀTYgWsޗNֹ矤cVs=ۓ9:3MrLx'6TSfqwjg>-cQ^ț/y;v_HjmE89 (35ŹծQjy՛VYlnR,P3 +ɹK'\1Q+bTvCC +Nl7'K;S9kbGDy[v^ݬ׬jEEKSj) U'ZSxԌȊCm5 1-]9\fՄAJ TP Vp5" C=¼ՓyF:zyj%lQ73ݹ?jc4ha9wܓ UQNٛݩZ1G#z"l<ͻ.j-M|aDYZSxԌȊF!/Zj  LYgWsޗCs?HOO-D&{'Sruf 6'4c8*l|Z-c6yVݗwk5QQRp`yֵ|T-̈TF!ZYe-VD(DLYjGjx}>1Q+bTvCC +Nl*l|Z-Dy[v]/wk5iSj)yֵ|T-2"RЇFaׯZܵXe@P\ Hԏ P3 +˹K!}Y9矤cVs=ۓ9:3LW}1UE;fnNwvs9hE%^{z^/WŖDT+\#rWyF:zyj%lQ73ݹ?jc4ha9wܓ W4-3Y6S9hثny;vt׬kk䵫j=z$ jDTb-H ǂN1~zR([˿LTnCCrLx'6İV[繷2Wa}<.fmn)Px|cT>t(|l,I_x| \?_O] .ܪf(E*1=ڥojjvezxRȣli(mOMONjbv##ZT6sY/sK5sQTsWj*mE9٣ Zt2zsUC#k]Iug;5?TTEEj*sUr"oU 5fSdҎ˻lW~[IrMn7O@Zْf%I֪qz>ٵ{TI-ܰzkt~^ H֦fW6ekXѥU] M^o7GyWCc4:eeU})cjrMʋ}kמe6=_h8O|;8ϻ}5_kk?ee7|D#o=^u6 ">Qb5s9fȧmku}sLnG+GΗ5UFNGԫi Vɕ_QQrv:x׷o3Q{c3RBd:1Zگsŷ+5\-boͯ_wN_.j:>6Ԫr#j%g0צ|˟:&O.pW-CtZGeQko.:&w2ɖW~۾r~UiE0zD$GzrnXwY|?o59(M'MVJӵ;Ђ&Er\g#Qj%l=M>wys{7SJԓKVY4 N]3{c?ϩ'[eTX?1ZF9|:z9n){_:_k3RJ覙E$nOMUvf3G>՞6v}U$ U$tխQl$,D/*lK/58۬t_cC}>?~[m;uj9G5RTVEC띟ܶgl+n{CI]P,zY_i/_6Tݱ6c'fy;LGJiPM[W>o<9\]K}[2;cJ.dcR$.hr4vUn7Z.gƛ<|9-7LMVmCя}=/}~t~_dvm2uYVīS\OWowk1{sA.&RK]E;G^YRHLҺe̫N{}ݸ]]5u2Vr[~qm{ dzOnE̦Ә57V>t)6n?u>٥2LZfIc"FDsw*5-چp}\lˏ%Y]~ѥߛHW@sdv%e\ٙ6Cn~Qw>Ck4j"ʏq#͑F#^E=|e'+j\qǡK=߬,vQ"?RDio4p¼Fڻ s= ߯GN'3ӫQ.TO7wxZqgährrzQM*I+DlkSWroկǻRrc'ӧón*^נv=OTYX+Z鱬{j*,{:n8ݯ/}jW4IOnF9Ujoj^'0r{~W?=;{ߘ;JY˙tfmoDžc˙?v}iţc}7$Xɕɳi\v /c9ϩ;]צ FTRTjVEֱ%Wӿ1cup6{y[ٽũj4ZE-u-Z՞J?"+X:&ɖ[^1-3|utemùhLҖfM_[ZJYǣ(`wf}+DM֮]u>e믫s=2c58Yiܨʨr]n[Z}gs\uOr(Fviu졉!wrcNvgfGow/{IpRMrtb-:՝TjVv{~/+=oَ3~uy D3'qinHNK*9-KwZ~l>/7C[ U*o̿v>?w''P2:V-.Gu2*S%U7.Wؖjd0DdPc؍kRDB*OpenSC-0.26.1/MacOSX/scripts/000077500000000000000000000000001474147347300155265ustar00rootroot00000000000000OpenSC-0.26.1/MacOSX/scripts/postinstall000077500000000000000000000036151474147347300200350ustar00rootroot00000000000000#!/bin/bash # 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 [ -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 OpenSC-0.26.1/MacOSX/target.plist000066400000000000000000000010011474147347300163720ustar00rootroot00000000000000 BundleHasStrictIdentifier BundleIsRelocatable BundleIsVersionChecked BundleOverwriteAction upgrade RootRelativeBundlePath Library/OpenSC/Applications/NotificationProxy.app OpenSC-0.26.1/MacOSX/target_startup.plist000066400000000000000000000002661474147347300201700ustar00rootroot00000000000000 OpenSC-0.26.1/MacOSX/target_token.plist000066400000000000000000000014001474147347300175750ustar00rootroot00000000000000 BundleHasStrictIdentifier BundleIsRelocatable BundleIsVersionChecked BundleOverwriteAction upgrade ChildBundles BundleOverwriteAction RootRelativeBundlePath Applications/Utilities/OpenSCTokenApp.app/Contents/PlugIns/OpenSCToken.appex RootRelativeBundlePath Applications/Utilities/OpenSCTokenApp.app OpenSC-0.26.1/MacOSX/target_tokend.plist000066400000000000000000000002661474147347300177520ustar00rootroot00000000000000 OpenSC-0.26.1/Makefile.am000066400000000000000000000027351474147347300150100ustar00rootroot00000000000000ACLOCAL_AMFLAGS = -I m4 MAINTAINERCLEANFILES = \ config.log config.status \ $(srcdir)/Makefile.in \ $(srcdir)/config.h.in $(srcdir)/config.h.in~ $(srcdir)/configure \ $(srcdir)/install-sh $(srcdir)/ltmain.sh $(srcdir)/missing \ $(srcdir)/depcomp $(srcdir)/aclocal.m4 \ $(srcdir)/config.guess $(srcdir)/config.sub \ $(srcdir)/m4/ltsugar.m4 $(srcdir)/m4/libtool.m4 \ $(srcdir)/m4/ltversion.m4 $(srcdir)/m4/lt~obsolete.m4 \ $(srcdir)/m4/ltoptions.m4 \ $(srcdir)/packaged EXTRA_DIST = Makefile.mak DISTCHECK_CONFIGURE_FLAGS = --with-completiondir=/tmp SUBDIRS = etc src win32 doc MacOSX tests dist_noinst_SCRIPTS = bootstrap bootstrap.ci dist_noinst_DATA = README \ packaging/debian.templates/changelog \ packaging/debian.templates/compat \ packaging/debian.templates/control \ packaging/debian.templates/copyright \ packaging/debian.templates/docs \ packaging/debian.templates/opensc.install \ 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)" if test -d "$(top_srcdir)/.git"; then \ echo "# Generated by Makefile. Do not edit!" > ChangeLog.tmp; \ $(GIT) log >> ChangeLog.tmp; \ else \ echo "Warning: Unable to generate ChangeLog. Need a Git repostiroy." >&2; \ echo > ChangeLog.tmp; \ fi mv ChangeLog.tmp "$(srcdir)/ChangeLog" ( cd "$(srcdir)" && autoreconf -ivf ) OpenSC-0.26.1/Makefile.mak000066400000000000000000000014011474147347300151500ustar00rootroot00000000000000SUBDIRS = etc win32 src default: all 32: CALL "C:\Program Files\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86 $(MAKE) /f Makefile.mak opensc.msi PLATFORM=x86 OPENPACE_DIR=C:\openpace-Win32_1.0.2 MOVE win32\OpenSC.msi OpenSC_win32.msi 64: CALL "C:\Program Files\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86_amd64 $(MAKE) /f Makefile.mak opensc.msi OPENPACE_DIR=C:\openpace-Win64_1.0.2 MOVE win32\OpenSC.msi OpenSC_win64.msi opensc.msi: $(MAKE) /f Makefile.mak all OPENSSL_DEF=/DENABLE_OPENSSL OPENPACE_DEF=/DENABLE_OPENPACE" @cmd /c "cd win32 && $(MAKE) /nologo /f Makefile.mak opensc.msi OPENSSL_DEF=/DENABLE_OPENSSL OPENPACE_DEF=/DENABLE_OPENPACE" all clean:: @for %i in ( $(SUBDIRS) ) do @cmd /c "cd %i && $(MAKE) /nologo /f Makefile.mak $@" OpenSC-0.26.1/NEWS000066400000000000000000001671221474147347300134550ustar00rootroot00000000000000NEWS for OpenSC -- History of user visible changes # 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) * Coolkey * JPKI (Japanese Individual Number Card) * nPA (German ID card, eSign Application) ## General Improvements * PKCS#15 * Implemented file caching based on card's contact-less UID * Cache EF.ODF and EF.TokenInfo * File caching is done transparently when the user sets the config option. * `opensc.conf` * Added `disable_popups` for disabling internal UI * All Windows specific reader configuration is handled by the pcsc driver (cardmod driver was removed) * Build Environment * Allow setting `PKG_CONFIG_PATH` for macOS build * Added compatibility with Visual Studio 2015 * Allow building against LibreSSL * Allow building against OpenSSL 1.1.0 * Allow building against WiX 3.11 * Allow building minidriver with MinGW * Include OpenPACE library by default * Removed `BUILD_ON`/`BUILD_FOR` variable * Simplified installer on macOS and Windows * Added support for PIN commands via PC/SC escape commands * Added support for card reader access via CryptoTokenKit * Added support for PIN entry on card for verification/unblock/change * Recognize T=0 limitation of sending 255 bytes * Force T=1 for contactless cards * Allow setting driver via `OPENSC_DRIVER` environment variable * Fixed many bugs * Fixed many compiler warnings * Fixed possible issues (memory corruptions, memory leaks, double free, ...) * Internal refactoring and cleanup ## PKCS#11 * Move PIN type label front of description * `C_GetTokenInfo` read the login status from the card if possible * Don't use ':' in the token name (#849) * Install `opensc-pkcs11.pc` for usage with `pkg-config` * Don't shrink the number of slots (#629) * Add session handle uniqueness check to PKCS#11 `C_OpenSession()` * Activate functionality of `C_WaitForSlot()` for pcsc-lite >= 1.8.22 ## Minidriver * Support PIN unblocking in minidriver via PUK as response * Added support for Session PIN ## Tokend * Allow usage of readers PIN pad by entering an empty PIN ## OpenSC Tools * Fixed Bash completion (#782) * `opensc-tool` * Added `--reset` option * `opensc-explorer` * Show tag 0x82 for unknown files * `pkcs15-tool` * Fixed `--read-ssh-key` crash (#788) * Added `--clear-cache` * Fixed locking the card on Windows (#868) * Add `--list-info` option * Make `--list-...` messages consistent * Add `--short` option * `--read-data-object`: Do not print data to terminal when output file is given * Reword `--no-prompt` to `--use-pinpad`, old option still available as alias * Added `--test-session-pin` option * `pkcs15-init` * Fix using PINPAD to verify PIN (#856) * Fixed locking the card on Windows (#868) * Added `--secret-key-algorithm` option * Print more detailed secret key information * `pkcs11-tool` * Added `keygen` for secret key generation * Better handling of PIN (re-) validation * Fixed --id for `C_GenerateKey`, DES and DES3 keygen mechanism (#857) * Added `--derive-pass-der` option * Added `--generate-random` option * Add GOSTR3410 key pair generation * `npa-tool` (new) * Allows read/write access to EAC tokens * Allows PIN management for EAC tokens * `gids-tool` * Fixed entering SN via command line * `sc-hsm-tool` * Added `--print-dkek-share` (hidden from the user) * Fixed locking the card on Windows (#868) ## CardOS * Better support for CardOS 5.3 ## DNIe * Fixed interaction with DNIe UI * Added support for DNIe 3.0 ## ePass2003 * Add new ATR for entersafe PKI card * Solved Incorrect PIN raise wrong CKR error ## GemsafeV1 * PTeid: add objects (SOD, TRACe, CA) and fix flags * PTeid: Support PIN max tries and tries left report * PTeid: Properly report cards with 2048b keys. ## MyEID * Fix to ECDH implementation (#756) * Added support for symmetric keys ## OpenPGP * Improve handling of OpenPGP card PIN change and unblock commands ## PIV * Some workarounds for PIV-alike cards (e.g. Yubikey) * Change driver's short name to 'PIV-II' * Use certificate's keyUsage to set PKCS#11 key attributes ## SC-HSM * Use PKCS#15 file cache * Prevent unnecessary applet selection and state resets * Added support for session pin * Fixed forcing a card driver via opensc.conf ## STARCOS * Read the maximum transcive sice from the card's ATR (#765) New in 0.16.0; 2016-05-15 * build link OpenSSL in static option: enable PKCS11 thread locking * configuration use one configuration file for all systems * tools: package revision as version ** pkcs11-tool keygen mechanism in pkcs11 tools write GOST public key fix CKA_SENSITIVE attribute of public keys ** opensc-explorer: added command find_tags allow ASN.1 decoding if the file seems incomplete ** pkcs15-tool: handle record-based files when doing file caching option to prine raw data ** sc-hsm-tool: status info support for SmartCard-HSM V2.0 ** doc: some missing options are documented, added documentation for gid tool * minidriver: support for ECC Windows x509 enrollment first implementation of CardDeleteContainer MD logs controlled by register and environment variable * reader-pcsc fixed unreleased locks with pcsc-lite honour PC/SC pt 10 dwMaxAPDUDataSize added call back for getting vendor/product id restrict access to card handles after fork SCardGetAttrib is used to initialize reader's metadata by default only short APDUs supported * pkcs11 no slot reserved for hot plug no more slot created 'per-applications' atomic operation (TODO: expand) export all C_* symbols metadata initialized from package info fix registering pkcs11 mechanisms multiple times sloppy initialization for C_GetSlotInfo * pkcs15 cache of on-card files extended to application paths configuration option to enable/disable application make file cache dir configurable in key info data type introduced 'auxiliary data' -- container for the non-pkc15 data. * OpenPGP support for Gnuk -- USB cryptographic token for GNU Privacy Guard build without OpenSSL implemented 'erase card' additional manufacturers * MyEID support for 521 bit ECC keys ATRs for the new cards * sc-hsm read/write support in minidriver * rtecp delete keys * GemSafeV1 support for European Patent Office smart card sign with SHA256 * Gids first support for Gids smart card * dnie * Feitian PKI card new ATRs * IsoApplet (fixes) * starcos initial support for STARCOS 3.4 (German D-Trust cards) * macosx install tokend to /Library/Security/ instead /System/Library/Security/ fixed locking issue in pcsc reader * PIV allow using of cards where default application in not PIV support for the Yubikey NEO * italian-CNS italian-cns reg file for minidriver New in 0.15.0; 2015-05-11 * new card drivers AzeDIT 3.5 IsoApplet MaskTech * libopensc allow extended length APDUs accept no output for 'SELECT' MF and 'SELECT' DF_NAME APDUs fixed sc_driver_version check adjusted send/receive size according to card capabilities in iso7816 make SELECT agnosting to sc_path_t's aid * asn1 support multi-bytes tags * pkcs15 reviewed support and tool functions for public key public certs and pubkeys with an auth_id are treated as private * pkcs11 introduced default PKCS#11 provider fetched real value of CKA_LOCAL for pubkey removed inconsistent attributes C_Digest issues no check if buffer too small before update * added support for Travis CI * updated support of EC in libopensc, pkcs15 and pkcs11 * fixed number of warnings, resource leaks, overity-scan issues * macosx target minimum OSX version to 10.7 update the minimal building instructions. locate and target the latest SDK to build against. locate the best newest SDK present on the computer. * build disable Secure Messaging if OpenSSL is not used * tools util_get_pin helper function * PIV Add AES support for PIV General Authenticate fixed invalid bit when writing PIV certificate object with gzipped certificate fixed bad caching behavior of PIV PKCS15 emulator * ePass2003 fixed failure due to re-authenticate of secure messaging when card is accessed by multiple PKCS11 sessions * MyEID EC support for MyEID-v4 card * openpgp extended options for openpgp-tool * asepcos fixed puk handling * sc-hsm support for Koblitz curves secp192k1 and secp256k1 (Bitcoin) improved error detection and reporting in sc-hsm-tool fixed Lc byte in VERIFY PIN block for PC/SC PIN PAD reader fix certificate delete bug * IAS/ECC fixed PKCS#11 compliance issues support for Morpho IAS Agent Card * cardos overwrite content of deleted private key * win32 setup improuvement look & feel custom actions with card registration minidriver impouvement fixed errors and warnings returned by Microsoft quality tool pin-pad support New in 0.14.0; 2014-05-31 * new card driver DNIe * extended existing drivers by support of Swedish eID card (gemsafeV1) EstEID 3.5 (mcrd) * bogus javacard driver removed * build return to the standard use of 'autoconf' CI specific bootstrap script: git commit stamp for the built packages windows friendly compile settings fixed a ton of compiler warnings fence against using EVP_sha256 mech debian packaging templates compile without OpenSSL and without SM enable compiler warnings by default win32 add 'VarFileInfo' block to version-info include to MSI package 'openpgp-tool.exe' 'version-info' resource for each target * macOSX "graphical uninstaller" to distribution DMG update package building to modern tools new tool and SDK paths for OS X 10.8 improved opensc-installer from distribution osx: target 10.9 (a free upgrade to anyone using 10.6+) from now on build 'fat' binaries i386 * common added getpass implementation for non windows * libopensc allow for the pin to be entered on the keypad during issuing introduce 'encoded-content' to the sc_file data general usage method to allocate generalized time * minidriver implemented 'CardChangeAuthenticator', 'CardGetChallenge' and 'CardUnblockPin' improved management of GUID use reader pin pad if available and allowed configuration options for compose GUID refuse create container mechanism add registers file for feitian cards fixed return code in 'CardGetContainerInfo' returned 'tries-left' for blocked card length of stripped data in RSADecrypt * pkcs#11 bind non-recognized card, generic 'init-token' procedure fixed CKA_VALUE of 'public-key' object fix ASN1 encoding issues PIN-NOT-INITIALIZED for the non-user PINs buffers overflow segfault due to the undefined 'application-file' * pkcs15 'direct' public key in PuKDF encoding implement SPKI public key encoding include and maintain minidriver framework data: cmap-record, md-flags, GUID, .. fixed encoding of 'SubjectPublicKeyInfo' DER encoding of 'issuer' and 'subject' PIN validation in 'pkcs15-verify' public key algorithm ECC public key encoding ECC ecpointQ * pkcs15init 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 fixed NULL pointer dereference error NULL 'store-key' handle ignore if no TokenInfo file to update set EC pubkey parameters from init data * reader-pcsc fixed implicit pin modification pin checking when implicitly given verify/modify pinpad commands * SM common SM 'increase-sequence-counter' procedure move SM APDU procedures to dedicated source file move SM common crypto procedures to the dedicated library * doc 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 * sc-hsm Added support for persistent EC public keys generated from certificate signing requests token label to be set via C_InitToken or sc-hsm-tool unblock PIN using C_InitPIN() initialize EC key params fixed bug that prevents a newly generated 2048 key to show up at the PKCS#11 interface bug when changing SO-PIN with opensc-explorer sc-hsm-tool memory checking and removed warning problem deleting CA certificates sc-hsm public key format returned when generating ECC keys sc-hsm-tool better error handling for non-SmartCard-HSM cards support for DKEK password sharing scheme threshold scheme parameters to manpage crash on Windows when --wrap-key frees memory allocated in opensc.dll * ias simplify the compute signature operation * PIV use SPKI encoding for public key data extract public key from cert if no object on card fix segfault and valgrind issue gen_key to expect the proper PIV Key references * CardOS build for Windows use information from AlgorithmInfo supported CardOS V5.0 * epass2003 key generation allows stricter privkey/pubkey ACLs list_files implemented properly disable padding allow exponents other than 65537 * myeid fixed file-id in myeid.profile * entersafe fix a bug when writing public key * EstEID match card only based on presence of application. * pteid do not call the iso7816 driver get_response operation * myeid support of EC key is broken New in 0.13.0; 2012-12-04 * New card driver ePass2003. * OpenPGP card: greatly improved card driver and PKCS#15 emulation; implemented write (pkcs15init) mode; greatly enhanced documentation and tools. * ECDSA keys supported in 'read' and 'write' modes by internal PKCS#15 library, PKCS#11 and tools. * Minidriver in 'write' mode. * SM: secure messaging in GlobalPlatform-SP01 and CW14890 specifications; supported by ePass2003, IAS/ECC and AuthentIC cards; "ACL" and "APDU" modes to trigger secure messaging session; 'local' version of the external secure messaging module. * PKCS#15: support of 'secret-key' PKCS#15 objects support of 'authentication-object' PKCS#15 objects support of 'algReference' common key PKCS#15 attribute support of 'algReference' common key PKCS#15 attribute support of 'subjectName' common public key PKCS#15 attribute * PKCS#11: removed 'onepin' version of pkcs#11 module configuration options to expose slots for PINs and present on-card applications. support GOSTR3410 generate key mechanism * Support of PACE reader. * Remove libltdl reference. * ECDSA supported by MyEID card * New card driver for the SmartCard-HSM, a light-weight hardware security module * New useful commands in 'opensc-explorer' tool: 'find', 'put-data', ... * fixed SIGV issue due to the unsupported public key format * fixes for the number of documentation issues New in 0.12.2; 2011-07-15 * Builds are now silent by default when OpenSC is built from source on Unix. * Using --wait with command line tools works with 64bit Linux again. * Greatly improved OpenPGP card support, including OpenPGP 2.0 cards like the one found in German Privacy Foundation CryptoStick. * Fixed support for FINeID cards issued after 01.03.2011 with 2048bit keys. * #256: Fixed support for TCOS cards (broken since 0.12.0). * Added support for IDKey-cards to TCOS3 driver. * #361: Improved PC/SC driver to fetch the maximum PIN sizes from the open source CCID driver. This fixes the issue for Linux/OSX with recent driver. * WindowsInstaller now installs only static DLL-s (PKCS#11, minidriver) to system folder. * Fix FINeID cards for organizations. * Several smaller bugs and compiler warnings fixed. New in 0.12.1; 2011-05-17 * New card driver: IAS/ECC 1.0.1 * rutoken-tool has been deprecated and removed. * eidenv and piv-tool utilities now have manual pages. * pkcs11-tool now requires the use of --module parameter. * All tools can now use an ATR as an argument to --reader, to skip to the card with given ATR. * opensc-tool -l with -v now shows information about the inserted cards. * Creating files have an enforced upper size limit, 64K * Support for multiple PKCS#15 applications with different AID-s. PKCS#15 applications can be listed with pkcs15-tool --list-applications. Binding to a specific AID with PKCS#15 tools can be done with --aid. * Hex strings (like card ATR or APDU-s) can now be separated by space, in addition to colons. * Pinpad readers known to be bogus are now ignored by OpenSC. At the moment only "HP USB Smart Card Keyboard" is disabled. * Windows installer is now distributed as a statically built MSI, for both x86 and x64. * Numerous compiler warnings, unused code and internal bugs have been eliminated. New in 0.12.0; 2010-12-22 * OpenSC uses a single reader driver, specified at compile time. * New card driver: Italian eID (CNS) by Emanuele Pucciarelli. * New card driver: Portuguese eID by João Poupino. * New card driver: westcos by François Leblanc. * pkcs11-tool can use a slot based on ID, label or index in the slot list. * PIN flags are updated from supported cards when C_GetTokenInfo is called. * Support for CardOS 4.4 cards added. * Feature to exclude readers from OpenSC PKCS#11 via "ignored_readers" configuration file entry. * #229: Support semi-automatic fixes to cards personalized with older and broken OpenSC versions. * Software keys removed from pkcs15-init and the PKCS#11 module. OpenSC can either generate keys on card or import plaintext keys to the card, but will never generate plaintext key material in software by itself. All traces of a software token (PKCS#15 Section 7) shall be removed. * Updates to PC/SC driver to build with pcsc-lite >= 1.6.2 * Build script for a binary Mac OS X installer for 10.5 and 10.6 systems. Binary installer includes OpenSC.tokend for platform integration. 10.6 installer includes engine_pkcs11. * Modify Rutoken S binary interfaces by Aktiv Co. * Support GOST R 34.10-2001 and GOST R 34.11-94 by Aktiv Co. * CardOS driver now emulates sign on rsa keys with sign+decrypt usage with padding and decrypt(). This is compatible with old cards and 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 (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. Please use generic PKCS#11 interface instead. * #include file statements cleaned up: first include "config.h", then system headers, then additional libraries, then headers in opensc (but from other directories), then header files from same directory. Fix path to reference headers, remove src/include/ directory. * Various source code fixes and improvements. * OpenSC now depends on xsltproc utility and docbook-xsl to build docs and man * Remove iconv dependency. EstEID driver now uses the commonName from the 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 New in 0.11.9; 2009-07-29; Andreas Jellinghaus * New rutoken_ecp driver by Aktiv Co. / Aleksey Samsonov * Allow more keys/certificates/files etc. with entersafe tokens * Updates pkcs11.h from scute fixing warnings * Small fixes in rutoken driver * Major update for piv driver with increased compatibility New in 0.11.8; 2009-05-07; Andreas Jellinghaus * Fix security problem in pkcs11-tool gen_keypair (PublicExponent 1) * fix compiling without openssl. * updated and improve entersafe driver. FTCOS/PK-01C cards are supported now, compatible with cards written by Feitian's software on windows. New in 0.11.7; 2009-02-26; Andreas Jellinghaus * hide_empty_slots now on by default? small logic change? * pinpad supported fixed for Mac OS X. * ruToken driver was updated. * openct virtual readers reduced to 2 by default. * link with iconv on Mac OS X for i18n support. * Security issue: Fix private data support. * Enable lock_login by default. * Disable allow_soft_keygen by default. 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 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). The build system can produce outputs for *NIX, cygwin and native windows (using mingw). * ruToken now supported. * Allow specifying application name for data objects. * Basic reader hotplug support. * PC/SC library is dynamic linked no longer compile time dependency. * PKCS#11 provider is now installed at LIBDIR/pkcs11 * PKCS#11 - Number of virtual slots moved into configuration. * PKCS#11 - Fix fork() compliance. * make sign_with_decrypt hack configurable for Siemens cards. New in 0.11.4; 2007-09-10; Andreas Jellinghaus * Drop AC_LIB_LINKFLAGS for libltdl and aclocal/lib* files. * New configure option to disable building nsplugin. * Support Siemens CardOS initialized cards (signing with decryption) * Add Siemens CardOS M4.2B support (experimental, don't have such a card) * Support for AKIS cards added (partial so far) by Gürer Özen. * add aclocal/libassuan.m4 back so developers don't need assuan installed. New in 0.11.3; 2007-07-11; Andreas Jellinghaus * added regression test for raw rsa (crypt0007). * regression suite can now use installed binaries with --installed. * update wiki export script (add images, fix links). * look for ncurses and termcap in configure (in combination with readline). * make lots of internal functions and variables static. * fix 0 vs NULL in many places. fix ansi c style (void). * avoid variable names used also as glibc function (random etc.). * new code for deleting objects. * special hack for firefox. * support for Athena APCOS cards added. * piv driver now supports bigger rsa keys too. New in 0.11.2; 2007-05-04; Andreas Jellinghaus * enabled pin caching by default (needed by regression suite and other apps). disable this for highest security (but that breaks some applications). * use max_send_size 255 / max_recv_size 256 bytes by default. reduce this for some readers (e.g. scm) with t=0 cards. * increase pin buffer size to allow longer pin codes. * Windows Make.rules.mak improved to work with and w/o openssl and zlib * Added --read-ssk-key option to pkcs15-tool (prints public key in ssh format) * use pkg-config for finding openct, add --enable/disable-openct option * use strlcpy function * use new pkcs11.h from scute with an open source license * add support for sha2 to pkcs15-crypt * add piv-tool for managing piv cards * add muscle driver (still work in progress) * improved oberthur driver * add support for pcsc v2 part10 (reader drivers with pinpad support) * convert source files to utf-8 New in 0.11.1; 2006-05-30; Andreas Jellinghaus * Fix version variable in win32 build files * Update for piv pkcs#15 emulation * Improved TCOS driver for Uni Giesen Card * Handle size_t printf with "%lu" and (unsigned long) cast * Add support for d-trust cards / improve micardo 2.1 driver New in 0.11.0; 2006-05-01; Andreas Jellinghaus * compile fixes/improvements for windows * document pkcs15-tool --unblock-pin option * remove old and outdated documentation * use "%lu" format for printf of size_t * add piv driver and tool by Douglas E. Engert * new threading code in pkcs11 module * renamed "etoken" driver to "cardos", as it really is a generic driver for Siemens CardOS M4, including but not limited to Aladdin eTokens. * add code to manage unused space * support for swedish nidel cards New in 0.10.1; 2006-01-08; Andreas Jellinghaus * use sc_print_path everywhere. * silence many warnings. * add incrypto34 driver by ST Incard, Giuseppe Amato * improved TCOS driver by Peter Koch * better PINPAD handling * updated infocamere driver * updated opensc.conf with new default values * fix firefox problems (no real fix, only ugly workaround) * add cardos M4.2 support New in 0.10.0; 2005-10-31; Andreas Jellinghaus * released rc2 without changes. * Add more documentation, fix man page installation. * New generic ATR/card matching code with atrmask support, used by all card drivers. * Much improved and unified ATR handling in the configuration file. * Support for the next generation FinEID cards with ISO/IEC 7816-15 data layout. * Preliminary code merge with the Belgian Belpic EID project. * Experimental multi-slot support for CT-API and dynamic loading support for win32. Thanks to Bernhard Froehlich * Experimental Class 2 pinpad reader support via TeleTrust compatible PC/SC interface. * Fixed OpenSSL behaviour in the configure script. * PKCS#15 emulation layer improvements and a new driver for the Italian postecert card. * New API documentation and generic documentation structure renovation to base future work on. Many thanks to Bert Vermeulen * Spanish manual translation from opensc-ceres project merged. * Several memory leaks and other bugs fixed. New in 0.9.6; 2005-04-25; Andreas Jellinghaus: * undo user_content changes to retain compatibility with 0.9.4. * add solaris/ files for easier installation on solaris. * Makefile.am: require automake 1.5 * free() fixes in some card drivers. * fix autoconf configure code. New in 0.9.5; 2005-01-11; Andreas Jellinghaus: * Big rewrite of the autoconf code for openssl. This fixes bugs on Mac OS X and we hope it doesn't break any other system. Feedback is very welcome. * The flags object attribute changed to a bitfield. * Many small bugfixes, including memory leaks. * Changes to the etoken and gpk profiles to eliminate overlapping file ids. * pinpad code by Martin Paljak * add user_consent parameter to pkcs15emu add object/add prkey functions. * estid provide user_consent parameter. * add fflush to pkcs11-spy.c * set version in configure.in, src/pkcs11/pkcs11-global.c, win32/version.rc and src/include/winconfig.h New in 0.9.4; 2004-10-31; Andreas Jellinghaus: * Library version was broken in 0.9.3. * Update library version to 1:0:0, as we are no longer compatible with the 0:*:* line, I fear. New in 0.9.3; 2004-10-31; Andreas Jellinghaus: * Fix some LDFLAGS/LDADD issues for parallel build. New in 0.9.2; 2004-07-24; Andreas Jellinghaus: * This is an beta test version. Please be careful. Do not use in production environments. * Fix sslengine, link those dynamically with libcrypto for openssl 0.9.7d and later. * Fixed small bug in pkcs11-tool * Link pkcs11-tool and pkcs15-crypt with -lcrypto * New driver for estonian ID card. * Bumped version number to opensc 0.9.2 * New card supported: Oberthur AuthentIC v5 * Pam_opensc's eid module now checks permissions, and supports several certificates in ~/.eid/authorized_certificates Thanks to Fritz Elfert * Upgrade library version to 0.9, since incompatible changes are very likely somewhere. * Merged several pkcs15 profiles into one with different options. New in 0.8.1; 2003-09-30; Olaf Kirch: * Upgrade libopensc versioning, hasn't been accidentally upgraded since 0.6.0 release * MacOS X specific changes: - Allow to compile without PC/SC support - Bundle installation fixes - OpenSSL engine linking fixed - Renamed OpenSC PKCS#11.bundle to opensc-pkcs11.bundle - CT-API module loading support * libopensc: - Renamed sysdep_timestamp_t to sc_timestamp_t - Renamed debug/error functions to sc_debug/sc_error - Don't DER-en/decode the data in a pkcs15 object - Portability fixes for the OpenCT reader driver * libscconf: Fixed CRLF parsing for UNIX platforms * Added PKCS#11 spy module by Mathias Brossard * Other minor bug/build fixes and cleanups New in 0.8.0; 2003-08-15; Juha Yrjölä: * New and/or improved card drivers: Aladdin eToken, MICARDO 2 and STARCOS * New reader driver: OpenCT (Olaf's framework) * Improved support for win32 and MacOS X. * PKCS #11 stuff improved massively * Added PKCS #11 and native OpenSC engine drivers for OpenSSL * Added support for reading the PIN from the PIN keypad of a reader * New manpages * Loads of other improvements and bug-fixes New in 0.7.0; 2002-06-03; Juha Yrjölä: * Support for config files * Yet another PKCS #15 generation rewrite * PAM module rewritten for more flexibility and compatibility * OpenSC Signer merged to the main source tree * CT-API support * Support for non-native RSA and DSA keys * Improved support for MioCOS cards by Miotec (http://www.miotec.fi) * Semi-working support for Aladdin eToken PRO * First version to work with OpenSSH without any patching New in 0.6.1; 2002-03-20; Juha Yrjölä: * Fixed certificate downloading in pkcs15-init * Improved PKCS #11 module, so it works with Mozilla 0.9.9 and is capable of signing and decrypting mails in Netscape * Other various small fixes and improvements New in 0.6.0; 2002-03-13; Juha Yrjölä: * Many, many new features -- too many to list here * New cards supported: Gemplus GPK family, TCOS 2.0, MioCOS * Implemented a card reader abstraction layer * PKCS #15 generation rewritten by Olaf Kirch. So far generation is supported only on GPK and Cryptoflex. New in 0.5.0; 2002-01-24; Juha Yrjölä: * PKCS #15 generation support * PKCS #11 module almost completely rewritten * Implemented opensc-explorer; a tool for browsing and modifying the card file system * Almost complete support for Cryptoflex 16k; implemented cryptoflex-tool * Started writing some API documentation using Doxygen * Much improved object handling code in PKCS #15 framework * Lots of bugs fixed, lots of new ones introduced New in 0.4.0; 2001-12-29; Juha Yrjölä: * Finished migrating to Autotools * Rewritten ASN.1 decoder (should work better on all PKCS #15 cards) * Abstracted card handling, so adding support for new cards is a whiz, 'opensc-tool -D' will list all installed drivers. * Added colored debug and error output ;) * Fixed some memory leaks * Support for Swedish Posten eID cards * Added very preliminary support for EMV compatible cards and Multiflex cards by Schlumberger New in 0.3.5; 2001-12-15; Juha Yrjölä: * Now compiles with C++ * Added card reset detection * Fixed PIN code changing * Improved certificate caching New in 0.3.2; 2001-11-27; Juha Yrjölä: * Converted to Autotools. OpenSC-0.26.1/README000077700000000000000000000000001474147347300150772README.mdustar00rootroot00000000000000OpenSC-0.26.1/README.md000066400000000000000000000122021474147347300142210ustar00rootroot00000000000000# OpenSC documentation 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. 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 # 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 64 bit and 32 bit programs (`OpenSC*_win64.msi` and `OpenSC*_win32.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) [![OSX build](https://github.com/OpenSC/OpenSC/actions/workflows/macos.yml/badge.svg)](https://github.com/OpenSC/OpenSC/actions/workflows/macos.yml) [![AppVeyor CI Build Status](https://ci.appveyor.com/api/projects/status/github/OpenSC/OpenSC?branch=master&svg=true)](https://ci.appveyor.com/project/frankmorgner/opensc/branch/master) [![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) | | SmartCardHSM | [![SmartCardHSM](https://gitlab.com/redhat-crypto/OpenSC/badges/sc-hsm/pipeline.svg)](https://gitlab.com/redhat-crypto/OpenSC/pipelines) | | ePass2003 | [![ePass2003](https://gitlab.com/redhat-crypto/OpenSC/badges/epass2003/pipeline.svg)](https://gitlab.com/redhat-crypto/OpenSC/pipelines) | OpenSC-0.26.1/SECURITY.md000066400000000000000000000016421474147347300145410ustar00rootroot00000000000000# 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.26.1 | :white_check_mark: | | < 0.26.1 | :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/) (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. OpenSC-0.26.1/bootstrap000077500000000000000000000000621474147347300147060ustar00rootroot00000000000000#!/bin/sh autoreconf --verbose --install --force OpenSC-0.26.1/bootstrap.ci000077500000000000000000000021441474147347300153030ustar00rootroot00000000000000#!/bin/sh usage() { cat << EOF usage: $0 options OpenSC bootstrap OPTIONS: -h Show this message -s Package suffix -S Use package suffix as 'g' appended with the date of last commit EOF } SUFFIX= while getopts “:hs:S” OPTION do case $OPTION in h) usage exit 1 ;; s) SUFFIX=$OPTARG ;; 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/'` ;; ?) usage exit ;; esac done set -e if [ -f Makefile ]; then make distclean fi rm -rf *~ *.cache config.guess config.log config.status config.sub depcomp ltmain.sh m4/version.m4.ci if [ -n "$SUFFIX" ] then echo Set package suffix "$SUFFIX" sed 's/^define(\[PACKAGE_SUFFIX\],\s*\[\([-~]*[0-9a-zA-Z]*\)\])$/define(\[PACKAGE_SUFFIX\], \['$SUFFIX'\])/g' < m4/version.m4 > m4/version.m4.ci fi ./bootstrap # autoreconf --verbose --install --force || true OpenSC-0.26.1/configure.ac000066400000000000000000001105501474147347300152350ustar00rootroot00000000000000dnl -*- mode: m4; -*- AC_PREREQ(2.68) define([PRODUCT_NAME], [OpenSC]) define([PRODUCT_TARNAME], [opensc]) define([PRODUCT_BUGREPORT], [https://github.com/OpenSC/OpenSC/issues]) define([PRODUCT_URL], [https://github.com/OpenSC/OpenSC]) define([PACKAGE_VERSION_MAJOR], [0]) define([PACKAGE_VERSION_MINOR], [26]) define([PACKAGE_VERSION_FIX], [1]) 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_sinclude(m4/version.m4.ci) # Hint: MacOSX/build-package.in 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 [subdir-objects]) OPENSC_VERSION_MAJOR="PACKAGE_VERSION_MAJOR" OPENSC_VERSION_MINOR="PACKAGE_VERSION_MINOR" OPENSC_VERSION_FIX="PACKAGE_VERSION_FIX" OPENSC_VS_FF_LEGAL_COPYRIGHT="VS_FF_LEGAL_COPYRIGHT" OPENSC_VS_FF_COMPANY_NAME="VS_FF_LEGAL_COMPANY_NAME" 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 changed/removed: OLDEST++) # (Interfaces added: CURRENT++, REVISION=0) OPENSC_LT_CURRENT="12" OPENSC_LT_OLDEST="12" OPENSC_LT_REVISION="2" OPENSC_LT_AGE="$((${OPENSC_LT_CURRENT}-${OPENSC_LT_OLDEST}))" AC_CONFIG_SRCDIR([src/libopensc/sc.c]) # silent build by default m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) AC_CANONICAL_HOST AC_PROG_CC # AC_PROG_CXX is needed to built the win32 custom action. Indeed dutil.h use [extern "C"] definition which fails on pure c compiler AC_PROG_CXX AC_PROG_OBJC PKG_PROG_PKG_CONFIG AC_C_BIGENDIAN AC_ARG_ENABLE( [optimization], [AS_HELP_STRING([--disable-optimization],[disable compile optimization @<:@enabled@:>@])], , [enable_optimization="yes"] ) AC_ARG_WITH( [cygwin-native], [AS_HELP_STRING([--with-cygwin-native],[compile native win32])], , [with_cygwin_native="no"] ) if test "${enable_optimization}" = "no"; then 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*) CPPFLAGS="${CPPFLAGS} -I/usr/local/include" LDFLAGS="${LDFLAGS} -L/usr/local/lib -R/usr/local/lib" ;; *-mingw*|*-winnt*) WIN32="yes" CPPFLAGS="${CPPFLAGS} -DWIN32_LEAN_AND_MEAN" WIN_LIBPREFIX="lib" ;; *-cygwin*) 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]) WIN_LIBPREFIX="cyg" AC_DEFINE([USE_CYGWIN], [1], [Define if you are on Cygwin]) fi ;; esac case "${host}" in *-mingw*|*-winnt*|*-cygwin*) DEBUG_FILE="%TEMP%\\\opensc-debug.log" PROFILE_DIR_DEFAULT="obtained from windows registers" PROFILE_DIR="\"\"" ;; *) DEBUG_FILE="/tmp/opensc-debug.log" PROFILE_DIR="\$(pkgdatadir)" PROFILE_DIR_DEFAULT="\$(pkgdatadir)" ;; esac case "${host}" in *-mingw*) CPPFLAGS="${CPPFLAGS} -D__USE_MINGW_ANSI_STDIO=1" ;; 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() 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 @<:@enabled@:>@])], , [enable_strict="yes"] ) AC_ARG_ENABLE( [pedantic], [AS_HELP_STRING([--enable-pedantic],[enable pedantic compile mode @<:@disabled@:>@])], , [enable_pedantic="no"] ) AC_ARG_ENABLE( [thread_locking], [AS_HELP_STRING([--disable-thread-locking],[disable OS thread locking @<:@enabled@:>@])], , [enable_thread_locking="yes"] ) AC_ARG_ENABLE( [zlib], [AS_HELP_STRING([--enable-zlib],[enable zlib linkage @<:@detect@:>@])], , [enable_zlib="detect"] ) AC_ARG_ENABLE( [readline], [AS_HELP_STRING([--enable-readline],[enable readline linkage @<:@detect@:>@])], , [enable_readline="detect"] ) AC_ARG_ENABLE( [openssl], [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@:>@])], , [enable_openpace="detect"] ) AC_ARG_ENABLE( [openct], [AS_HELP_STRING([--enable-openct],[enable openct linkage @<:@disabled@:>@])], , [enable_openct="no"] ) AC_ARG_ENABLE( [pcsc], [AS_HELP_STRING([--disable-pcsc],[disable pcsc support @<:@enabled@:>@])], , [enable_pcsc="yes"] ) AC_ARG_ENABLE( [cryptotokenkit], [AS_HELP_STRING([--disable-cryptotokenkit],[disable CryptoTokenKit support @<:@enabled@:>@])], , [enable_cryptotokenkit="no"] ) AC_ARG_ENABLE( [ctapi], [AS_HELP_STRING([--enable-ctapi],[enable CT-API support @<:@disabled@:>@])], , [enable_ctapi="no"] ) AC_ARG_ENABLE( [minidriver], [AS_HELP_STRING([--enable-minidriver],[enable minidriver on Windows @<:@disabled@:>@])], , [enable_minidriver="no"] ) AC_ARG_ENABLE( [sm], [AS_HELP_STRING([--disable-sm],[disable secure messaging support and SM modules @<:@enabled@:>@])], , [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@:>@])], , [enable_man="detect"] ) AC_ARG_ENABLE( [doc], [AS_HELP_STRING([--enable-doc],[enable installation of documents @<:@disabled@:>@])], , [enable_doc="no"] ) AC_ARG_ENABLE( [tests], [AS_HELP_STRING([--enable-tests],[enable tests @<:@enabled@:>@])], , [enable_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@:>@])], , [enable_dnie_ui="no"] ) AC_ARG_ENABLE( [notify], [AS_HELP_STRING([--enable-notify],[enable notifications @<:@detect@:>@])], , [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( [xsl-stylesheetsdir], [AS_HELP_STRING([--with-xsl-stylesheetsdir=PATH],[docbook xsl-stylesheets for svn build @<:@detect@:>@])], [xslstylesheetsdir="${withval}"], [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@:>@])], , [with_pcsc_provider="detect"] ) AC_ARG_WITH( [pkcs11-provider], [AS_HELP_STRING([--with-pkcs11-provider=PATH],[Path to the default PKCS11 provider @<:@default=OpenSC@:>@])], , [with_pkcs11_provider="detect"] ) dnl ./configure check 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!]) fi dnl Checks for programs. AC_PROG_CPP AC_PROG_INSTALL AC_PROG_LN_S AC_PROG_MKDIR_P AC_PROG_SED AC_PROG_MAKE_SET dnl Add libtool support. ifdef( [LT_INIT], [ LT_INIT([win32-dll]) LT_LANG([Windows Resource]) ], [ AC_LIBTOOL_WIN32_DLL AC_LIBTOOL_RC AC_PROG_LIBTOOL ] ) dnl These required for repository checkout AC_ARG_VAR([XSLTPROC], [xsltproc utility]) AC_ARG_VAR([git], [git]) AC_CHECK_PROGS([XSLTPROC],[xsltproc]) AC_CHECK_PROGS([GIT],[git]) AC_MSG_CHECKING([xsl-stylesheets]) if test "${xslstylesheetsdir}" = "detect"; then xslstylesheetsdir="no" for f in \ /usr/share/xml/docbook/stylesheet/nwalsh \ /usr/share/xml/docbook/stylesheet/nwalsh/current \ /opt/local/share/xsl/docbook-xsl \ /sw/share/xml/xsl/docbook-xsl \ /usr/share/sgml/docbook/*; do test -e "${f}/html/docbook.xsl" && xslstylesheetsdir="${f}" done elif test "${xslstylesheetsdir}" != "no"; then test -e "${xslstylesheetsdir}/html/docbook.xsl" || AC_MSG_ERROR([invalid]) fi AC_MSG_RESULT([${xslstylesheetsdir}]) AC_MSG_CHECKING([git checkout]) GIT_CHECKOUT="no" if test -n "${GIT}" -a -d "${srcdir}/.git"; then GIT_CHECKOUT="yes" fi AC_MSG_RESULT([${GIT_CHECKOUT}]) if test "${GIT_CHECKOUT}" = "yes"; then REVISION_DESCRIPTION="$(${GIT} describe || echo '' )" if test "${REVISION_DESCRIPTION}" = ""; then REVISION_DESCRIPTION="$(${GIT} describe --tags || echo '')" fi HASH_COMMIT_DATE="$(${GIT} log -1 --pretty=format:'rev: %h, commit-time: %ci')" GIT_TAG_COMMIT="$(${GIT} rev-list --tags --no-walk --max-count=1)" 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" fi dnl C Compiler features AC_C_INLINE dnl Checks for header files. 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 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 dnl Checks for library functions. AC_FUNC_ERROR_AT_LINE AC_FUNC_STAT AC_FUNC_VPRINTF AC_CHECK_FUNCS([ \ getpass gettimeofday getline memset mkdir \ 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" else LIBRARY_BITNESS="32" fi dnl See if socket() is found from libsocket AC_CHECK_LIB( [socket], [socket], [ LIBS="${LIBS} -lsocket" AC_CHECK_LIB( [resolv], [res_query], [LIBS="${LIBS} -lresolv"] ) ] ) if test "${WIN32}" = "no"; then dnl dl support 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( [AC_DEFINE( [HAVE_PTHREAD], [1], [Define if you have POSIX threads libraries and header files.] )], [AC_MSG_ERROR([POSIX thread support required])] ) CC="${PTHREAD_CC}" fi if test "${enable_thread_locking}" = "yes"; then OPENSC_PKCS11_PTHREAD_CFLAGS="${PTHREAD_CFLAGS} -DPKCS11_THREAD_LOCKING" else OPENSC_PKCS11_PTHREAD_CFLAGS="" fi AC_SUBST(OPENSC_PKCS11_PTHREAD_CFLAGS) if test "${enable_minidriver}" = "yes"; then dnl win32 special test for minidriver AC_CHECK_HEADER( [cardmod.h], , [AC_MSG_ERROR([cardmod.h from CNG is required for minidriver])], [#if defined(__MINGW32__) #include "${srcdir}/src/minidriver/cardmod-mingw-compat.h" #endif ]) AC_DEFINE([ENABLE_MINIDRIVER], [1], [Enable minidriver support]) 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-*) 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 AC_CHECK_LIB( [z], [inflate], [ZLIB_LIBS="-lz"] ) fi saved_CFLAGS="${CFLAGS}" CFLAGS="${CFLAGS} ${ZLIB_CFLAGS}" AC_CHECK_HEADERS([zlib.h]) CFLAGS="${saved_CFLAGS}" test -n "${ZLIB_LIBS}" -a "${ac_cv_header_zlib_h}" = "yes" && have_zlib="yes" case "${enable_zlib}" in no) have_zlib="no" ;; detect) if test "${have_zlib}" = "yes"; then enable_zlib="yes" else enable_zlib="no" fi ;; esac if test "${enable_zlib}" = "yes"; then if test "${have_zlib}" = "yes"; then AC_DEFINE([ENABLE_ZLIB], [1], [Use zlib libraries and header files]) else AC_MSG_ERROR([zlib linkage required, but no zlib was found]) fi fi AC_ARG_VAR([READLINE_CFLAGS], [C compiler flags for readline]) AC_ARG_VAR([READLINE_LIBS], [linker flags for readline]) if test -z "${READLINE_LIBS}"; then for l in "" -lncurses -ltermcap; do unset ac_cv_lib_readline_readline AC_CHECK_LIB( [readline], [readline], [READLINE_LIBS="-lreadline ${l}"], , ["${l}"] ) test -n "${READLINE_LIBS}" && break; done fi saved_CFLAGS="${CFLAGS}" CFLAGS="${CFLAGS} ${READLINE_CFLAGS}" AC_CHECK_HEADERS([readline/readline.h]) CFLAGS="${saved_CFLAGS}" test -n "${READLINE_LIBS}" -a "${ac_cv_header_readline_readline_h}" = "yes" && have_readline="yes" case "${enable_readline}" in no) have_readline="no" ;; detect) if test "${have_readline}" = "yes"; then enable_readline="yes" else enable_readline="no" fi ;; esac if test "${enable_readline}" = "yes"; then if test "${have_readline}" = "yes"; then AC_DEFINE([ENABLE_READLINE], [1], [Use readline libraries and header files]) else AC_MSG_ERROR([readline linkage required, but no readline was found]) fi fi PKG_CHECK_MODULES( [OPENSSL], [libcrypto >= openssl_minimum_version], [have_openssl="yes"], [AC_CHECK_LIB( [crypto], [RSA_get_version], [ have_openssl="yes" OPENSSL_LIBS="-lcrypto" ], [have_openssl="no"] )] ) case "${enable_openssl}" in no) have_openssl="no" ;; detect) saved_CFLAGS="${CFLAGS}" CFLAGS="${CFLAGS} ${OPENSSL_CFLAGS}" AC_CHECK_HEADERS([openssl/crypto.h],,[have_openssl="no"]) CFLAGS="${saved_CFLAGS}" if test "${have_openssl}" = "yes"; then enable_openssl="yes" else enable_openssl="no" fi ;; esac if test "${enable_openssl}" = "yes"; then if test "${have_openssl}" = "yes"; then AC_DEFINE([ENABLE_OPENSSL], [1], [Have OpenSSL libraries and header files]) else AC_MSG_ERROR([OpenSSL linkage required, but no OpenSSL was found]) fi else OPENSSL_CFLAGS="" 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])], [AC_MSG_WARN([libeac not found by pkg-config])]) saved_CPPFLAGS="$CPPFLAGS" saved_LIBS="$LIBS" CPPFLAGS="$CPPFLAGS $OPENPACE_CFLAGS" LIBS="$LDFLAGS $OPENPACE_LIBS" have_openpace="yes" AC_CHECK_HEADERS(eac/eac.h, [], [ AC_MSG_WARN([OpenPACE headers not found]) have_openpace="no" ]) 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, AS_HELP_STRING([--enable-cvcdir=DIR], [directory containing CV certificates (default is determined by libeac)]), [cvcdir="${enableval}"], [cvcdir=false]) if test "${cvcdir}" = false ; then cvcdir="`$PKG_CONFIG libeac --variable=cvcdir`" fi if test "${cvcdir}" = "" ; then case "${host}" in *-mingw*|*-winnt*|*-cygwin*) cvcdir="%PROGRAMFILES%\\\OpenSC Project\\\OpenSC\\\cvc" ;; *) AC_MSG_WARN([use --enable-cvcdir=DIR]) ;; esac fi CVCDIR="${cvcdir}" AC_SUBST(CVCDIR) AC_DEFINE_UNQUOTED([CVCDIR], ["${CVCDIR}"], [CVC directory]) AC_ARG_ENABLE(x509dir, AS_HELP_STRING([--enable-x509dir=DIR], [directory containing X.509 certificates (default is determined by libeac)]), [x509dir="${enableval}"], [x509dir=false]) if test "${x509dir}" = false ; then x509dir="`$PKG_CONFIG libeac --variable=x509dir`" fi if test -z "${x509dir}" then x509dir="`$PKG_CONFIG libeac --variable=x509dir`" fi if test -z "${x509dir}" then case "${host}" in *-mingw*|*-winnt*|*-cygwin*) x509dir="%PROGRAMFILES%\\\OpenSC Project\\\OpenSC\\\x509" ;; *) AC_MSG_WARN([use --enable-x509dir=DIR]) ;; esac fi X509DIR="${x509dir}" AC_SUBST(X509DIR) AC_DEFINE_UNQUOTED([X509DIR], ["${X509DIR}"], [CVC directory]) case "${enable_openpace}" in no) have_openpace="no" ;; detect) if test "${have_openpace}" = "yes"; then enable_openpace="yes" else enable_openpace="no" fi ;; esac if test "${enable_openpace}" = "yes"; then if test "${have_openpace}" = "yes"; then AC_DEFINE([ENABLE_OPENPACE], [1], [Use OpenPACE libraries and header files]) else AC_MSG_ERROR([OpenPACE linkage required, but no OpenPACE was found]) fi else OPENPACE_CFLAGS="" OPENPACE_LIBS="" fi if test "${enable_piv_sm}" = "yes"; then AC_DEFINE([ENABLE_PIV_SM], [1], [Enable PIV SM]) fi if test "${enable_openct}" = "yes"; then PKG_CHECK_MODULES( [OPENCT], [libopenct], [AC_DEFINE([ENABLE_OPENCT], [1], [Have OpenCT libraries and header files])], [AC_MSG_ERROR([openct requested but not available])] ) fi if test "${enable_ctapi}" = "yes"; then AC_DEFINE([ENABLE_CTAPI], [1], [Enable CT-API support]) 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*) # Locate the latest SDK. SDK_PATH=$(xcrun --sdk macosx --show-sdk-path) PCSC_CFLAGS="-I$SDK_PATH/System/Library/Frameworks/PCSC.framework/Versions/Current/Headers" ;; *) PCSC_CFLAGS="-I/usr/include/PCSC" ;; esac fi fi saved_CFLAGS="${CFLAGS}" CFLAGS="${CFLAGS} ${PCSC_CFLAGS}" # We must cope with mingw32 that does not have winscard.h mingw64 has it. AC_CHECK_HEADERS([winscard.h],,[test "${WIN32}" != "yes" && AC_MSG_ERROR([winscard.h is required for pcsc])]) AC_CHECK_HEADERS([pcsclite.h]) CFLAGS="${saved_CFLAGS}" if test "${with_pcsc_provider}" = "detect"; then case "${host}" in *-*-darwin*) DEFAULT_PCSC_PROVIDER="/System/Library/Frameworks/PCSC.framework/PCSC" ;; *-mingw*|*-winnt*|*-cygwin*) DEFAULT_PCSC_PROVIDER="winscard.dll" ;; *) DEFAULT_PCSC_PROVIDER="libpcsclite.so.1" ;; esac else DEFAULT_PCSC_PROVIDER="${with_pcsc_provider}" fi AC_DEFINE_UNQUOTED([DEFAULT_PCSC_PROVIDER], ["${DEFAULT_PCSC_PROVIDER}"], [Default PC/SC provider]) AC_DEFINE([ENABLE_PCSC], [1], [Define if PC/SC is to be enabled]) fi if test "${enable_cryptotokenkit}" = "yes"; then if test -z "${CRYPTOTOKENKIT_CFLAGS}"; then case "${host}" in *-apple-*) CRYPTOTOKENKIT_CFLAGS="-framework CryptoTokenKit -framework Foundation" LDFLAGS="${LDFLAGS} -framework CryptoTokenKit -framework Foundation" ;; *) AC_MSG_ERROR([CryptoTokenKit only supported on Darwin]) ;; esac fi 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_SUBST(DYN_LIB_EXT) AC_SUBST(LIBDIR) AC_SUBST(LIB_PRE) case "${host}" in *-mingw*|*-winnt*|*-cygwin*) DYN_LIB_EXT=".dll" LIBDIR="" LIB_PRE="" ;; *) DYN_LIB_EXT=".so" LIBDIR="\$(libdir)/" LIB_PRE="lib" ;; esac if test "${enable_sm}" = "yes"; then AC_DEFINE([ENABLE_SM], [1], [Enable secure messaging support]) DEFAULT_SM_MODULE="${LIB_PRE}smm-local${DYN_LIB_EXT}" case "${host}" in *-mingw*|*-winnt*|*-cygwin*) DEFAULT_SM_MODULE_PATH="%PROGRAMFILES%\\\OpenSC Project\\\OpenSC\\\tools" ;; *) DEFAULT_SM_MODULE_PATH="${libdir}" ;; esac fi if test "${with_pkcs11_provider}" = "detect"; then 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 if test "${enable_man}" = "detect"; then if test "${WIN32}" = "yes"; then enable_man="no" elif test -n "${XSLTPROC}" -a "${xslstylesheetsdir}" != "no"; then enable_man="yes" else enable_man="no" fi fi if test "${enable_man}" = "yes" -o "${enable_doc}" = "yes"; then AC_MSG_CHECKING([XSLTPROC requirement]) test -n "${XSLTPROC}" || AC_MSG_ERROR([Missing XSLTPROC]) test "${xslstylesheetsdir}" != "no" || AC_MSG_ERROR([Missing xslstylesheetsdir]) AC_MSG_RESULT([ok]) fi 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 OPENSC_FEATURES="${OPENSC_FEATURES} locking" fi if test "${enable_zlib}" = "yes"; then OPENSC_FEATURES="${OPENSC_FEATURES} zlib" OPTIONAL_ZLIB_CFLAGS="${ZLIB_CFLAGS}" OPTIONAL_ZLIB_LIBS="${ZLIB_LIBS}" fi if test "${enable_readline}" = "yes"; then OPENSC_FEATURES="${OPENSC_FEATURES} readline" OPTIONAL_READLINE_CFLAGS="${READLINE_CFLAGS}" OPTIONAL_READLINE_LIBS="${READLINE_LIBS}" fi if test "${enable_openssl}" = "yes"; then OPENSC_FEATURES="${OPENSC_FEATURES} openssl" OPTIONAL_OPENSSL_CFLAGS="${OPENSSL_CFLAGS}" OPTIONAL_OPENSSL_LIBS="${OPENSSL_LIBS}" fi if test "${enable_openct}" = "yes"; then OPENSC_FEATURES="${OPENSC_FEATURES} openct" OPTIONAL_OPENCT_CFLAGS="${OPENCT_CFLAGS}" OPTIONAL_OPENCT_LIBS="${OPENCT_LIBS}" fi if test "${enable_pcsc}" = "yes"; then OPENSC_FEATURES="${OPENSC_FEATURES} pcsc(${DEFAULT_PCSC_PROVIDER})" OPTIONAL_PCSC_CFLAGS="${PCSC_CFLAGS}" fi if test "${enable_cryptotokenkit}" = "yes"; then OPTIONAL_CRYPTOTOKENKIT_CFLAGS="${CRYPTOTOKENKIT_CFLAGS}" fi if test "${enable_ctapi}" = "yes"; then OPENSC_FEATURES="${OPENSC_FEATURES} ctapi" fi if test "${enable_minidriver}" = "yes"; then AC_MSG_CHECKING([WiX SDK]) AC_CHECK_HEADERS([wcautil.h],[enable_minidriver_ca="yes"],[enable_minidriver_ca="no"]) if test "${enable_minidriver_ca}" = "yes"; then AC_MSG_RESULT([found, minidriver setup custom action will be built]) else AC_MSG_RESULT([not found, minidriver setup custom action will be skipped]) fi else enable_minidriver_ca="no" fi AC_DEFINE_UNQUOTED([OPENSC_VERSION_MAJOR], [${OPENSC_VERSION_MAJOR}], [OpenSC version major component]) AC_DEFINE_UNQUOTED([OPENSC_VERSION_MINOR], [${OPENSC_VERSION_MINOR}], [OpenSC version minor component]) AC_DEFINE_UNQUOTED([OPENSC_VERSION_FIX], [${OPENSC_VERSION_FIX}], [OpenSC version fix component]) AC_DEFINE_UNQUOTED([OPENSC_VERSION_REVISION], [${OPENSC_VERSION_REVISION}], [OpenSC file version revision]) AC_DEFINE_UNQUOTED([OPENSC_SCM_REVISION], ["${OPENSC_SCM_REVISION}"], [OpenSC version Git describe revision]) AC_DEFINE_UNQUOTED([OPENSC_FEATURES], ["${OPENSC_FEATURES}"], [Enabled OpenSC features]) AC_DEFINE_UNQUOTED([OPENSC_VS_FF_LEGAL_COPYRIGHT], ["${OPENSC_VS_FF_LEGAL_COPYRIGHT}"], [OpenSC version-info LegalCopyright value]) AC_DEFINE_UNQUOTED([OPENSC_VS_FF_COMPANY_NAME], ["${OPENSC_VS_FF_COMPANY_NAME}"], [OpenSC version-info CompanyName value]) 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]) pkcs11dir="\$(libdir)/pkcs11" AC_SUBST([pkcs11dir]) AC_SUBST([xslstylesheetsdir]) AC_SUBST([OPENSC_VERSION_MAJOR]) AC_SUBST([OPENSC_VERSION_MINOR]) AC_SUBST([OPENSC_VERSION_FIX]) AC_SUBST([OPENSC_VERSION_REVISION]) AC_SUBST([OPENSC_SCM_REVISION]) AC_SUBST([OPENSC_VS_FF_LEGAL_COPYRIGHT]) 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]) AC_SUBST([OPENSC_LT_AGE]) 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]) AC_SUBST([OPTIONAL_READLINE_LIBS]) AC_SUBST([OPTIONAL_OPENSSL_CFLAGS]) AC_SUBST([OPTIONAL_OPENSSL_LIBS]) AC_SUBST([OPTIONAL_OPENCT_CFLAGS]) AC_SUBST([OPTIONAL_OPENCT_LIBS]) AC_SUBST([OPTIONAL_PCSC_CFLAGS]) AC_SUBST([LIBRARY_BITNESS]) AC_SUBST([DEFAULT_SM_MODULE]) 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"]) 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([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="-pedantic ${CFLAGS}" fi if test "${enable_strict}" = "yes"; then 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/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/tools/versioninfo-opensc-notify.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/Distribution_universal.xml MacOSX/resources/Welcome.html ]) AC_OUTPUT cat < OpenSC-0.26.1/doc/files/000077500000000000000000000000001474147347300146145ustar00rootroot00000000000000OpenSC-0.26.1/doc/files/Makefile.am000066400000000000000000000030641474147347300166530ustar00rootroot00000000000000MAINTAINERCLEANFILES = $(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 $@ 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 OpenSC-0.26.1/doc/files/files.html000066400000000000000000002644571474147347300166260ustar00rootroot00000000000000OpenSC 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)

  • tokend: Configuration block for macOS' tokend (OpenSC.tokend)

  • 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).

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 PIV Card

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, flex (deactivated driver), cyberflex (deactivated driver), gemsafeV1, idprime, itacns, jpki, MaskTech, mcrd (deactivated driver), npa, nqapplet, tcos 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.

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: ignore in Tokend, protect otherwise).

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 Tokend

score = num;

Score for OpenSC.tokend (Default: 300). The tokend with the highest score shall be used.

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... ;

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)

OpenSC-0.26.1/doc/files/files.xml000066400000000000000000000005261474147347300164430ustar00rootroot00000000000000 OpenSC Manual Pages: Section 5 OpenSC-0.26.1/doc/files/opensc.conf.5.xml.in000066400000000000000000001716131474147347300203320ustar00rootroot00000000000000 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) tokend: Configuration block for macOS' tokend (OpenSC.tokend) 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 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). 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 tokend: 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 PIV Card 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, flex (deactivated driver), cyberflex (deactivated driver), gemsafeV1, idprime, itacns, jpki, MaskTech, mcrd (deactivated driver), npa, nqapplet, tcos 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. 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: ignore in Tokend, protect otherwise). 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 Tokend Score for OpenSC.tokend (Default: 300). The tokend with the highest score shall be used. 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 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 OpenSC-0.26.1/doc/files/pkcs15-profile.5.xml.in000066400000000000000000000665031474147347300206640ustar00rootroot00000000000000 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 OpenSC-0.26.1/doc/html.xsl000066400000000000000000000007321474147347300152100ustar00rootroot00000000000000 ]> OpenSC-0.26.1/doc/man.xsl000066400000000000000000000002531474147347300150150ustar00rootroot00000000000000 OpenSC-0.26.1/doc/tools/000077500000000000000000000000001474147347300146525ustar00rootroot00000000000000OpenSC-0.26.1/doc/tools/Makefile.am000066400000000000000000000035651474147347300167170ustar00rootroot00000000000000MAINTAINERCLEANFILES = $(srcdir)/Makefile.in EXTRA_DIST = completion-template dist_noinst_DATA = $(wildcard $(srcdir)/*.xml) if ENABLE_DOC html_DATA = tools.html endif if ENABLE_MAN man1_MANS = $(patsubst $(srcdir)/%.xml, %, $(wildcard $(srcdir)/*.1.xml)) endif completion_DATA = $(patsubst $(srcdir)/%.1.xml, %, $(wildcard $(srcdir)/*.1.xml)) tools.html: $(srcdir)/tools.xml $(wildcard $(srcdir)/*.1.xml) $(AM_V_GEN)$(XSLTPROC) --nonet --path "$(srcdir)/..:$(xslstylesheetsdir)/html" --xinclude -o $@ html.xsl $< 2>/dev/null %.1: $(srcdir)/%.1.xml $(AM_V_GEN)sed -e 's|@pkgdatadir[@]|$(pkgdatadir)|g' < $< \ | $(XSLTPROC) --nonet --path "$(srcdir)/..:$(xslstylesheetsdir)/manpages" --xinclude -o $@ man.xsl - 2>/dev/null %: $(srcdir)/%.1.xml $(AM_V_GEN)cat $(srcdir)/completion-template \ | sed "s,ALLOPTS,\ $(shell sed -n 's,.*.*,\1,pg' $< \ | sort -u | grep -- '^\-' | tr '\n' ' ')," \ | sed "s,OPTSWITHARGS,\ $(shell sed -n 's,.*.*.*,\1,pg' $< \ | sort -u | grep -- '^\-' | tr '\n' '|' | sed 's,|$$,,' | grep ^ || echo "!*")," \ | sed "s,FILEOPTS,\ $(shell sed -n 's,.*.*.*filename.*,\1,pg' $< \ | sort -u | grep -- '^\-' | tr '\n' '|' | sed 's,|$$,,' | grep ^ || echo "!*")," \ | sed "s,PINOPTS,\ $(shell sed -En 's,.*.*\s*(newpin|pin|puk|sopin|sopuk)\s*<.*,\1,pg' $< \ | sort -u | grep -- '^\-' | tr '\n' '|' | sed 's,|$$,,' | grep ^ || echo "!*")," \ | sed "s,MODULEOPTS,\ $(shell sed -n 's,.*.*.*mod.*,\1,pg' $< \ | sort -u | grep -- '^\-' | tr '\n' '|' | sed 's,|$$,,' | grep ^ || echo "!*")," \ | sed "s,FUNCTION_NAME,$(shell echo $@ | sed s,-,_,g)," \ | sed "s,PROGRAM_NAME,$@," \ > $@ clean-local: -rm -rf $(html_DATA) $(man1_MANS) $(completion_DATA) OpenSC-0.26.1/doc/tools/cardos-tool.1.xml000066400000000000000000000063631474147347300177710ustar00rootroot00000000000000 cardos-tool 1 OpenSC OpenSC Tools opensc cardos-tool displays information about Card OS-based security tokens or format them 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 the card or token. , Print help message on screen. , Display information about the card or token. arg, 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. arg, arg Specify startkey for format. arg, arg Change Startkey with given APDU command. , Causes cardos-tool to be more verbose. Specify this flag several times to enable debug output in the opensc library. , Causes cardos-tool to wait for the token to be inserted into reader. Authors cardos-tool was written by Andreas Jellinghaus aj@dungeon.inka.de. OpenSC-0.26.1/doc/tools/completion-template000066400000000000000000000022651474147347300205640ustar00rootroot00000000000000# this was auto-generated by OpenSC/doc/tools/Makefile.am _FUNCTION_NAME() { COMPREPLY=() local cur prev split=false _get_comp_words_by_ref -n : cur prev _split_longopt && split=true opts="ALLOPTS" if [ ${COMP_CWORD} -eq 1 ]; then COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 fi case "${prev}" in MODULEOPTS) _filedir so return 0 ;; FILEOPTS) _filedir return 0 ;; PINOPTS|--password) COMPREPLY=( $( compgen -W "$(printenv | cut -d = -f 1 | xargs printf 'env:%s ')" -- $cur ) ) __ltrim_colon_completions "$cur" return 0 ;; OPTSWITHARGS) return 0 ;; *) COMPREPLY=($(compgen -W "${opts}" -- ${cur})) return 0 ;; esac $split && return 0 if [[ "$cur" == -* ]]; then _longopt $1 return fi _filedir } complete -F _FUNCTION_NAME PROGRAM_NAME # Local variables: # mode: shell-script # sh-basic-offset: 4 # sh-indent-comment: t # indent-tabs-mode: nil # End: # ex: ts=4 sw=4 et filetype=sh OpenSC-0.26.1/doc/tools/cryptoflex-tool.1.xml000066400000000000000000000133231474147347300207070ustar00rootroot00000000000000 cryptoflex-tool 1 OpenSC OpenSC Tools opensc cryptoflex-tool utility for manipulating Schlumberger Cryptoflex data structures 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 num, num Specifies the DF to operate in arg, arg Creates new RSA key files for arg keys id, id Creates new PIN file for CHVid exp, exp Specifies the RSA exponent, exp, to use in key generation. The default value is 3. , Generate a new RSA key pair num, num Specifies the key number to operate on. The default is key number 1. , Lists all keys stored in a public key file length, length Specifies the modulus length to use in key generation. The default value is 1024. id, id Specifies the private key file id, id, to use id, id Specifies the public key file id, id, to use , Reads a public key from the card, allowing the user to extract and store or use the public key arg, 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. , Causes cryptoflex-tool to be more verbose. Specify this flag several times to enable debug output in the opensc library. , Verifies CHV1 before issuing commands , Causes cryptoflex-tool to wait for a card insertion. See also pkcs15-tool 1 Authors cryptoflex-tool was written by Juha Yrjölä juha.yrjola@iki.fi. OpenSC-0.26.1/doc/tools/dnie-tool.1.xml000066400000000000000000000100431474147347300174230ustar00rootroot00000000000000 dnie-tool 1 OpenSC OpenSC Tools opensc dnie-tool displays information about DNIe based security tokens dnie-tool OPTIONS Description The dnie-tool utility is used to display additional information about DNIe, the Spanish National eID card. Options , Show the DNIe IDESP value. , Show DNIe personal information. Reads and print DNIe number and User Name and SurName , Displays every available information. This command is equivalent to -d -i -V -s , Displays DNIe Serial Number , Show DNIe sw version. Displays software version for in-card DNIe OS pin, 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. arg, 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. , Causes dnie-tool to wait for the token to be inserted into reader. , 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 jonsito@terra.es. OpenSC-0.26.1/doc/tools/dtrust-tool.1.xml000066400000000000000000000104471474147347300200410ustar00rootroot00000000000000 dtrust-tool 1 OpenSC OpenSC Tools opensc dtrust-tool displays information about D-Trust signature cards and remove the transport protection dtrust-tool OPTIONS Description The dtrust-tool utility is used to display information about D-Trust signature cards and to remove the initial transport protection. Options , In the delivery state the card is locked by a so called transport protection. This option allows to check if the transport protection is still in force. The Signature PIN can only be used if the transport protection is removed. Initially the transport protection should be intact. If you receive a new card and the transport protection was already broken, don't use that card and contact the producer for further advice. If you removed the transport protection, it is normal that dtrust-tool reports the transport protection as broken. This is the normal operation state. It does not mean your card is broken. , Print help message on screen. arg, 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. , Show the status of the various PINs. The Card Holder PIN is used for advanced signatures and decryption. It is only defined for signature cards, but not for sealing cards. The signature PIN is used for qualified signatures. It can only be used if it is unlocked by presenting the Transport PIN. Once the Transport PIN is used, it cannot be used anymore. The PUK is used to unlock PIN which had beend entered incorrectly several times. , This command removes the transport protection. If first queries for the Transport PIN and then for the new value of the Signature PIN twice. , Causes dtrust-tool to be more verbose. Specify this flag several times to enable debug output in the opensc library. , Causes dtrust-tool to wait for the token to be inserted into reader. Authors dtrust-tool was written by Mario Haustein mario.haustein@hrz.tu-chemnitz.de. OpenSC-0.26.1/doc/tools/egk-tool.1.xml000066400000000000000000000064071474147347300172630ustar00rootroot00000000000000 egk-tool 1 OpenSC OpenSC Tools opensc egk-tool displays information on the German electronic health card (elektronische Gesundheitskarte, eGK) egk-tool OPTIONS Description The egk-tool utility is used to display information stored on the German elektronic health card (elektronische Gesundheitskarte, eGK). Options , Print help and exit. , Print version and exit. arg, 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. , Causes egk-tool to be more verbose. Specify this flag several times to be more verbose. 'Gesundheitsanwendung', Health Care Application (<abbrev>HCA</abbrev>) Show 'Persönliche Versichertendaten' (XML). Show 'Allgemeine Versichertendaten' (XML). Show 'Geschützte Versichertendaten' (XML). Show 'Versichertenstammdaten-Status'. Authors egk-tool was written by Frank Morgner frankmorgner@gmail.com. OpenSC-0.26.1/doc/tools/eidenv.1.xml000066400000000000000000000061341474147347300170110ustar00rootroot00000000000000 eidenv 1 OpenSC OpenSC Tools opensc eidenv utility for accessing visible data from electronic identity cards 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 prog, prog Executes the given program with data in environment variables. , Print help message on screen. , Prints all data fields from the card, like validity period, document number etc. arg, 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. , Prints key usage statistics (only for Estonian ID card). , Prints the version of the utility and exits. , Wait for a card to be inserted Authors eidenv utility was written by Stef Hoeben and Martin Paljak martin@martinpaljak.net. OpenSC-0.26.1/doc/tools/gids-tool.1.xml000066400000000000000000000102101474147347300174260ustar00rootroot00000000000000 gids-tool 1 OpenSC OpenSC Tools opensc gids-tool smart card utility for GIDS cards 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 , Initialize token. argument Define the administrator key 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. argument Define serial number. , Unblock the user PIN after an administrator authentication. , Change the administrator key. argument Define the new administrator key. argument, 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. , Wait for a card to be inserted. , Verbose operation. Use several times to enable debug output. See also opensc-tool 1 Authors gids-tool was written by Vincent Le Toux vincent.letoux@mysmartlogon.com. OpenSC-0.26.1/doc/tools/goid-tool.1.xml000066400000000000000000000176051474147347300174410ustar00rootroot00000000000000 goid-tool 1 OpenSC OpenSC Tools opensc goid-tool smart card utility for GoID fingerprint card goid-tool OPTIONS mode Description The goid-tool utility can be used from the command line to read and write data of the GoID fingerprint card. Options , Print help message on screen. , Print the OpenSC package release version. string, string Specify the number of the reader to use. By default, the first reader with present card is used. If the argument is an ATR, the reader with a matching card will be chosen. , Cause goid-tool to be more verbose. Use it multiple times to be even more verbose. , Verify PIN. , Verify finger print. Verify PIN or finger print (user's choice). Options for SoCManager Applet Change PIN Use (several times) to change one or more biometric templates Dump Information about the SoCManager's configuration Options for PAccess Applet filename, filename Use (several times) to pass CV certificates , , Private key for the CV certificate Print the card ID data Write the specified card ID Print the PAccess ID data Write the specified PAccess ID id Read the specified data group; use several times to read out multiple files filename Write output to a file instead of printing it; use once for each use of id Write the specified data group; use several times to write multiple files filename Read input from a file; use once for each use of id Delete the specified data group; use several times to delete multiple files id Create the specified data group; use several times to create multiple files size File size of newly created DGs condition Access condition for reading newly created DGs condition Access condition for reading newly created DGs index Required access bit in certificate's CHAT for reading newly created DGs condition Access condition for writing newly created DGs index Required access bit in certificate's CHAT for writing newly created DGs See also opensc.conf 5 Authors goid-tool was written by Frank Morgner frankmorgner@gmail.com. OpenSC-0.26.1/doc/tools/iasecc-tool.1.xml000066400000000000000000000053671474147347300177500ustar00rootroot00000000000000 iasecc-tool 1 OpenSC OpenSC Tools opensc iasecc-tool displays information about IAS/ECC card iasecc-tool OPTIONS Description The iasecc-tool utility is used to display information about IAS/ECC v1.0.1 smart cards. Options 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. , Get list of the on-card applications. hex-aid, Select hex-aid before processing. sdo-type, List SDOs of the given sdo-type, present in default or selected application. , Causes cardos-tool to be more verbose. Specify this flag several times to enable debug output in the opensc library. , Causes iasecc-tool to wait for the token to be inserted into reader. Authors iasecc-tool was written by Viktor Tarasov viktor.tarasov@gmail.com. OpenSC-0.26.1/doc/tools/netkey-tool.1.xml000066400000000000000000000215451474147347300200140ustar00rootroot00000000000000 netkey-tool 1 OpenSC OpenSC Tools opensc netkey-tool administrative utility for Netkey E4 cards 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 , Displays a short help message. pin, pin Specifies the current value of the global PIN. pin, pin Specifies the current value of the global PUK. pin, pin Specifies the current value of the local PIN0 (aka local PIN). pin, pin Specifies the current value of the local PIN1 (aka local PUK). arg, 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. Causes netkey-tool to be more verbose. This options may be specified multiple times to increase verbosity. PIN format With the , , or the 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 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 pk_opensc@web.de. OpenSC-0.26.1/doc/tools/npa-tool.1.xml000066400000000000000000000355521474147347300172760ustar00rootroot00000000000000 npa-tool 1 OpenSC OpenSC Tools opensc npa-tool displays information on the German eID card (neuer Personalausweis, nPA). npa-tool OPTIONS Description The npa-tool utility is used to display information stored on the German eID card (neuer Personalausweis, nPA), 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 other identity cards and machine readable travel documents (MRTDs) may be read as well. Options , Print help and exit. , Print version and exit. arg, 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. , Causes npa-tool to be more verbose. Specify this flag several times to be more verbose. Password Authenticated Connection Establishment (<abbrev>PACE</abbrev>) STRING, STRING Run PACE with (transport) eID-PIN. STRING, STRING Run PACE with PUK. STRING, STRING Run PACE with Card Access Number (CAN). STRING, STRING Run PACE with Machine Readable Zone (MRZ). Enter the MRZ without newlines. 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 STRING, STRING Install a new PIN. , Resume eID-PIN (uses CAN to activate last retry). (default=off) , Unblock PIN (uses PUK to activate three more retries). (default=off) Terminal Authentication (<abbrev>TA</abbrev>) and Chip Authentication (<abbrev>CA</abbrev>) FILENAME, 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. HEX_STRING Certificate description to show for Terminal Authentication. 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). HEX_STRING, HEX_STRING Specify the terminal's auxiliary data. If not given, the default is determined by verification of validity, age and community ID. FILENAME, FILENAME Specify the terminal's private key. 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. DIRECTORY Specify where to look for the X.509 certificate. If not given, it defaults to /home/fm/.local/etc/eac/x509. Disable checking the validity period of CV certificates. (default=off) Disable passive authentication. (default=off) Read and write data groups Read data group 1: Document Type. Read data group 2: Issuing State. Read data group 3: Date of Expiry. Read data group 4: Given Name(s). Read data group 5: Family Name. Read data group 6: Religious/Artistic Name. Read data group 7: Academic Title. Read data group 8: Date of Birth. Read data group 9: Place of Birth. Read data group 10: Nationality. Read data group 11: Sex. Read data group 12: Optional Data. Read data group 13: Birth Name. Read data group 14. Read data group 15. Read data group 16. Read data group 17: Normal Place of Residence. Read data group 18: Community ID. Read data group 19: Residence Permit I. Read data group 20: Residence Permit II. Read data group 21: Optional Data. HEX_STRING Write data group 17: Normal Place of Residence. HEX_STRING Write data group 18: Community ID. HEX_STRING Write data group 19: Residence Permit I. HEX_STRING Write data group 20: Residence Permit II. HEX_STRING Write data group 21: Optional Data. Verification of validity, age and community ID YYYYMMDD Verify chip's validity with a reference date. YYYYMMDD Verify age with a reference date. HEX_STRING Verify community ID with a reference ID. Special options, not always useful , Brute force PIN, CAN or PUK. Use together with options , , or . (default=off) FILENAME, FILENAME Specify the file with APDUs of HEX_STRINGs to send through the secure channel. (default=`stdin') Force compliance to BSI TR-03110 version 2.01. (default=off) Disable all checking of fly-by-data. (default=off) Authors npa-tool was written by Frank Morgner frankmorgner@gmail.com. OpenSC-0.26.1/doc/tools/openpgp-tool.1.xml000066400000000000000000000164321474147347300201640ustar00rootroot00000000000000 openpgp-tool 1 OpenSC OpenSC Tools opensc openpgp-tool utility for accessing visible data OpenPGP smart cards and compatible tokens 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 , Show card information. arg Delete key indicated by arg. arg can be 1, 2, 3, or all. arg, 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 (i.e. reset) the card. prog, prog Execute the given program with data in environment variables. arg, arg Generate key with the ID given as arg. arg can be one of 1, 2, or 3. , Print help message on screen. , Show information of keys on the card. keytype, 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 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. Print values in pretty format. Print values in raw format, as they are stored on the card. arg, 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. , Show card holder information. pintype Verify PIN (CHV1, CHV2 or CHV3). , Print the version of the utility and exit. , Verbose operation. Use several times to enable debug output. , Wait for a card to be inserted. Authors openpgp-tool utility was written by Peter Marschall peter@adpm.de. OpenSC-0.26.1/doc/tools/opensc-asn1.1.xml000066400000000000000000000031551474147347300176660ustar00rootroot00000000000000 opensc-asn1 1 OpenSC OpenSC Tools opensc opensc-asn1 parse ASN.1 data opensc-asn1 OPTIONS FILES Description The opensc-asn1 utility is used to parse ASN.1 data. Options , Print help and exit. , Print version and exit. Authors opensc-asn1 was written by Frank Morgner frankmorgner@gmail.com. OpenSC-0.26.1/doc/tools/opensc-explorer.1.xml000066400000000000000000000636561474147347300207000ustar00rootroot00000000000000 opensc-explorer 1 OpenSC OpenSC Tools opensc opensc-explorer generic interactive utility for accessing smart card and similar security token functions 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. driver, 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. path, 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. arg, 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. , Cause opensc-explorer to be more verbose. Specify this flag several times to enable debug output in the opensc library. , 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. 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. 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 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. 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ä juha.yrjola@iki.fi. OpenSC-0.26.1/doc/tools/opensc-notify.1.xml000066400000000000000000000072761474147347300203440ustar00rootroot00000000000000 opensc-notify 1 OpenSC OpenSC Tools opensc opensc-notify monitor smart card events and send notifications opensc-notify OPTIONS Description The opensc-notify utility is used to monitor smart card events and send the appropriate notification. Options , Print help and exit. , Print version and exit. Mode: customized Send customized notifications. STRING, STRING Specify the title of the notification. STRING, STRING Specify the main text of the notification. Mode: standard Manually send standard notifications. , See notify_card_inserted in opensc.conf (default=off). , See notify_card_removed in opensc.conf (default=off). , See notify_pin_good in opensc.conf (default=off). , See notify_pin_bad in opensc.conf (default=off). Authors opensc-notify was written by Frank Morgner frankmorgner@gmail.com. OpenSC-0.26.1/doc/tools/opensc-tool.1.xml000066400000000000000000000141141474147347300177760ustar00rootroot00000000000000 opensc-tool 1 OpenSC OpenSC Tools opensc opensc-tool generic smart card utility 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 Print the OpenSC package release version. , Print the Answer To Reset (ATR) of the card. Output is in hex byte format driver, driver Use the given card driver. The default is to auto-detect the correct card driver. The literal value ? lists all available card drivers. , Lists algorithms supported by card , Print information about OpenSC, such as version and enabled components. , List all installed card drivers. , Recursively list all files stored on card. , List all configured readers. , Print the name of the inserted card (driver). conf, conf Get configuration key, format: section:name:key conf, conf Set configuration key, format: section:name:key:value arg, 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. type, Resets the card in reader. The default reset type is cold, but warm reset is also possible. apdu, 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 default. Print the card serial number (normally the ICCSN). Output is in hex byte format , Causes opensc-tool to be more verbose. Specify this flag several times to enable debug output in the opensc library. , Wait for a card to be inserted. See also opensc-explorer 1 Authors opensc-tool was written by Juha Yrjölä juha.yrjola@iki.fi. OpenSC-0.26.1/doc/tools/piv-tool.1.xml000066400000000000000000000164631474147347300173160ustar00rootroot00000000000000 piv-tool 1 OpenSC OpenSC Tools opensc piv-tool smart card utility for HSPD-12 PIV cards piv-tool OPTIONS 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 800-73-3. It is intended for use with test cards only. It can be used to 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 Print the card serial number derived from the CHUID object, if any. Output is in hex byte format. , Print the name of the inserted card (driver) argument, 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:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX argument, 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 or 9E and alg is 06, 07, 11 or 14 for RSA 1024, RSA 2048, ECC 256 or ECC 384 respectively. ContainerID, ContainerID Load an object onto the card. The ContainerID is as defined in NIST 800-73-n without leading 0x. Example: CHUID object is 3000 ref, ref Load a certificate onto the card. ref is 9A, 9C, 9D or 9E ref, ref Load a certificate that has been gzipped onto the card. ref is 9A, 9C, 9D or 9E file, file Output file for any operation that produces output. file, file Input file for any operation that requires an input file. file Print properties of the key slots. Needs 'admin' authentication. apdu, apdu Sends an arbitrary APDU to the card in the format AA:BB:CC:DD:EE:FF.... This option may be repeated. arg, 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 for a card to be inserted , Causes piv-tool to be more verbose. Specify this flag several times to enable debug output in the opensc library. See also opensc-tool 1 Authors piv-tool was written by Douglas E. Engert deengert@gmail.com. OpenSC-0.26.1/doc/tools/pkcs11-register.1.xml000066400000000000000000000063721474147347300204670ustar00rootroot00000000000000 pkcs11-register 1 OpenSC OpenSC Tools opensc pkcs11-register Simple tool to install PKCS#11 modules to known applications. pkcs11-register OPTIONS Description The pkcs11-register utility can be used from the command line to register PKCS#11 modules to various applications Options , Print help message on screen. , Print the OpenSC package release version. filename, filename Path to the PKCS#11 module to load. The default is OpenSC PKCS#11 module. Don't install module for Chrome browser. By default, the tool attempts to install the module for Chrome browser. Don't install module for Firefox browser. By default, the tool attempts to install the module for Firefox browser. Don't install module for Thunderbird mail client. By default, the tool attempts to install the module for Thunderbird mail client. Don't install module for Seamonkey. By default, the tool attempts to install the module Seamonkey. See also pkcs11-tool 1 opensc.conf 5 Authors pkcs11-register was written by Frank Morgner frankmorgner@gmail.com. OpenSC-0.26.1/doc/tools/pkcs11-tool.1.xml000066400000000000000000000670661474147347300176270ustar00rootroot00000000000000 pkcs11-tool 1 OpenSC OpenSC Tools opensc pkcs11-tool utility for managing and using PKCS #11 security tokens 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 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 the user PIN on the token Unlock User PIN (without unlock in logged in session; otherwise has to be 'context-specific'). , Hash some data. 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 Specify the id of the object to operate on. Initializes the user PIN. This option differs from in that it sets the user PIN for the first time. Once set, the user PIN can be changed using . Initialize a token: set the token label as well as a Security Officer PIN (the label must be specified using ). filename, filename Specify the path to a file for input. , Generate a new key pair (public and private pair.) Generate a new key. 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, GOSTR3410-2012-256:B, DES:8, DES3:24, AES:16, AES: or GENERIC:64. If the key type was incompletely specified, possible values are listed. Specify 'sign' key usage flag (sets SIGN in privkey, sets VERIFY in pubkey). Specify 'decrypt' key usage flag. For RSA keys, sets DECRYPT in privkey and ENCRYPT in pubkey. For secret keys, sets both DECRYPT and ENCRYPT. Specify 'derive' key usage flag (EC only). Specify 'wrap' key usage flag. name, name Specify the name of the object to operate on (or the token label when is used). , Display a list of mechanisms supported by the token. , Display a list of objects. The options , , or can be used to filter the listed objects. , Display a list of available slots on the token. , List slots with tokens. List interfaces of PKCS #11 3.0 library. , Forces to open the PKCS#11 session with CKF_RW_SESSION. , Authenticate to the token before performing other operations. This option is not needed if a PIN is provided on the command line. Specify login type ('so', 'user', 'context-specific'; default:'user'). mechanism, mechanism Use the specified mechanism for token operations. See for a list of mechanisms supported by your token. The mechanism can also be specified in hexadecimal, e.g., 0x80001234. 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. mod Specify a PKCS#11 module (or library) to load. filename, filename Test a Mozilla-like key pair generation and certificate request. Specify the filename to the certificate file. filename, filename Specify the path to a file for output. pin, 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 option. puk Supply User PUK on the command line. pin Supply new User PIN on the command line. 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) id, id Set the CKA_ID of the object. , Display general token information. , Sign some data. , Decrypt some data. , Encrypt some data. , Unwrap key. , Wrap key. , Derive a secret key using another key and some data. , Derive ECDHpass DER encoded pubkey for compatibility with some PKCS#11 implementations 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). id Specify the id of the slot to use (accepts HEX format with 0x.. prefix or decimal number). description Specify the description of the slot to use. index Specify the index of the slot to use. index Specify the index of the object to use. Tell pkcs11 module it should use OS thread locking. options Test a pkcs11 module's thread implication. (See source code). label Specify the label of token. Will be used the first slot, that has the inserted token with this label. 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 also applies here. , Perform some tests on the token. This option is most useful when used with either or . Test hotplug capabilities (C_GetSlotList + C_WaitForSlotEvent). Set the CKA_PRIVATE attribute (object is only viewable after a login). 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. 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 (best used with the or option). Test forking and calling C_Initialize() in the child. type, type Specify the type of object to operate on. Valid value are cert, privkey, pubkey, secrkey and data. , 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 signature of some data. , Get object's CKA_VALUE attribute (use with ). , Delete an object. label Specify the application label of the data object (use with data). id Specify the application ID of the data object (use with data). data Specify the issuer in hexadecimal format (use with cert). data Specify the subject in hexadecimal format (use with cert/privkey/pubkey). filename The path to the signature file for signature verification format Format for ECDSA signature: 'rs' (default), 'sequence', 'openssl'. filename, filename Write a key or certificate object to the token. filename points to the DER-encoded certificate or key file. num Get num bytes of random data. Allow using software mechanisms that do not have the CKF_HW flag set. May be required when using software tokens and emulators. data Initialization vector for symmetric ciphers. The data is hexadecimal number, i.e. "000013aa7bffa0". num Sets the length of the MAC for the general-length MACing mechanisms to num bytes. data Additional authenticated data for AEAD ciphers. The data is an hexadecimal number. num Sets the length of the tag for AEAD ciphers to num bits. filename Specify the file containing the salt for HKDF (optional) filename Specify the file containing the info for HKDF (optional) 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 --applicatin-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 okir@suse.de. OpenSC-0.26.1/doc/tools/pkcs15-crypt.1.xml000066400000000000000000000175751474147347300200170ustar00rootroot00000000000000 pkcs15-crypt 1 OpenSC OpenSC Tools opensc pkcs15-crypt perform crypto operations using PKCS#15 smart cards 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 , Print the OpenSC package release version. aid Specify the AID of the on-card PKCS#15 application to bind to. The aid must be in hexadecimal form. , Decrypt the contents of the file specified by the option. The result of the decryption operation is written to the file specified by the 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 ). file, file Specifies the input file to use. Defaults to stdin if not specified. id, id Selects the ID of the key to use. file, file Any output will be sent to the specified file. Defaults to stdout if not specified. pin, 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. 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 option, however, pkcs15-crypt will perform the required padding using the algorithm outlined in the PKCS #1 standard version 1.5. , Outputs raw 8 bit data. arg, 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. 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. , Perform digital signature operation on the data read from a file specified using the 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 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 ). , 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). , Causes pkcs15-crypt to wait for a card insertion. , 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ä juha.yrjola@iki.fi. OpenSC-0.26.1/doc/tools/pkcs15-init.1.xml000066400000000000000000000743711474147347300176160ustar00rootroot00000000000000 pkcs15-init 1 OpenSC OpenSC Tools opensc pkcs15-init 1 OpenSC OpenSC Tools opensc pkcs15-init smart card personalization utility 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 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 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 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. If you are generating an EC key, the curve designation must be specified, for example ec:prime256v1. 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 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 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 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 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 if needed. Options , Print the OpenSC package release version. name, name Tells pkcs15-init to load the specified card profile option. You will rarely need this option. , This tells pkcs15-init to create a PKCS #15 structure on the card, and initialize any PINs. SERIAL Specify the serial number of the card. , 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. AID This will erase the application with the application identifier AID. keyspec, 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 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 option. pin, puk, sopin, 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. , Do not install a SO PIN, and do not prompt for it. name, 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 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. 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. filename, filename Tells pkcs15-init to store the certificate given in on the card, creating a certificate object with the ID specified via the 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 option. , Store a new PIN/PUK on the card. 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 . By default, the file is assumed to contain the key in PEM format. Alternative formats can be specified using . filename, 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 . It is a good idea to specify the key ID along with this command, using the 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 option. 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 option. You may additionally specify the key ID along with this command, using the 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 option. filename, filename Store a data object. filename, filename Tells pkcs15-init to update the certificate object with the ID specified via the 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! arg, 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 - must also be specified, in the other cases an 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 arg, arg Tells pkcs15-init to change the specified attribute. arg is either privkey, pubkey, secrkey, cert or data. You also have to specify the of the object. For now, you can only change the , e.g: pkcs15-init -A cert --id 45 -a 1 --label Jim , Tells pkcs15-init to not ask for the transport keys and use default keys, as known by the card driver. Tells pkcs15-init to perform a card specific sanity check and possibly update procedure. arg, 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. , Causes pkcs15-init to be more verbose. Specify this flag several times to enable debug output in the OpenSC library. , Causes pkcs15-init to wait for a card insertion. Do not prompt the user; if no PINs supplied, pinpad will be used. filename, filename Specify ID of PIN to use/create ID Specify ID of PUK to use/create 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. LABEL Specify label of PUK LABEL Specify public key label (use with ) LABEL Specify user cert label (use with ) arg Specify application name of data object (use with ) AID Specify AID of the on-card PKCS#15 application to be binded to (in hexadecimal form) filename filename, Output public portion of generated key to file PASSPHRASE Specify passphrase for unlocking secret key Mark certificate as a CA certificate arg 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 , Finish initialization phase of the smart card Update 'lastUpdate' attribute of tokenInfo When storing PKCS#12 ignore CA certificates Store or update existing certificate Private key stored as an extractable key 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 mode: do not require a PIN for private key GUID For a new key specify GUID for a MD container , Display help message See also pkcs15-profile 5 Authors pkcs15-init was written by Olaf Kirch okir@suse.de. OpenSC-0.26.1/doc/tools/pkcs15-tool.1.xml000066400000000000000000000314261474147347300176220ustar00rootroot00000000000000 pkcs15-tool 1 OpenSC OpenSC Tools opensc pkcs15-tool utility for manipulating PKCS #15 data structures on smart cards and similar security tokens 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 Print the OpenSC package release version. aid Specify in a hexadecimal form the AID of the on-card PKCS#15 application to bind to. id, id Specifies the auth id of the PIN to use for the operation. This is useful with the --change-pin operation. Changes a PIN or PUK stored on the token. User authentication is required for this operation. , List all card objects. List card objects. List the on-card PKCS#15 applications. , List all certificates stored on the token. , 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 option has to be used. , 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 option has to be used. 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 option has to be used. List all PINs stored on the token. General information about each PIN is listed (eg. PIN name). Actual PIN values are not shown. List all public keys stored on the token, including key name, id, algorithm and length information. , Output lists in compact format. Disables token data caching. Removes the user's cache directory. On Windows, this option additionally removes the system's caching directory (requires administrator privileges). filename, 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. Changes how prints the content to standard output. By default, when is not given, it will print the content in hex notation. If is set, it will print the binary data directly. This does not affect the output that is written to the file specified by the option. Data written to a file will always be in raw binary. cert Reads the certificate with the given id. data, data Reads data object with OID, applicationName or label. The content is printed to standard output in hex notation, unless the option is given. If an output file is given with the option, the content is additionally written to the file. Output to the file is always written in raw binary mode, the only affects standard output behavior. id Reads the public key with id id, allowing the user to extract and store or use the public 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. When used in conjunction with option the output format of the public key follows rfc4716. The default output format is a single line (openssh). , Test if the card needs a security update , Update the card with a security update 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. , Unblocks a PIN stored on the token. Knowledge of the Pin Unblock Key (PUK) is required for this operation. , Causes pkcs15-tool to be more verbose. Specify this flag several times to enable debug output in the OpenSC library. pin, newpin, 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. pin Specify New PIN (when changing or unblocking) Verify PIN after card binding and before issuing any command (without 'auth-id' the first non-SO, non-Unblock PIN will be verified) Equivalent to with additional session PIN generation , Causes pkcs15-tool to wait for a card insertion. 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ä juha.yrjola@iki.fi. OpenSC-0.26.1/doc/tools/sc-hsm-tool.1.xml000066400000000000000000000353631474147347300177120ustar00rootroot00000000000000 sc-hsm-tool 1 OpenSC OpenSC Tools opensc sc-hsm-tool smart card utility for SmartCard-HSM 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 token, removing all existing keys, certificates and files. Use to define SO-PIN for first initialization or to verify in subsequent initializations. Use to define the initial user pin value. Use to define the maximum number of wrong user PIN presentations. Use with to enable key wrap / unwrap. Use with to define a token label Use with and to require public key authentication for login filename, filename Create a DKEK share encrypted under a password and save it to the file given as parameter. Use to provide a password for encryption rather than prompting for one. Use and to randomly generate a password and split is using a (t, n) threshold scheme. filename, filename Prompt for user password, read and decrypt DKEK share and import into SmartCard-HSM. Use to provide a password for decryption rather than prompting for one. Use to specify the number of shares that should be entered to reconstruct the password. filename, filename Wrap the key referenced in and save with it together with the key description and certificate to the given file. Use to provide the user PIN on the command line. filename, filename Read wrapped key, description and certificate from file and import into SmartCard-HSM under the key reference given in . Determine the key reference using the output of pkcs15-tool -D. Use to provide a user PIN on the command line. Use to remove any key, key description or certificate in the way. number-of-shares, number-of-shares Define the number of DKEK shares to use for recreating the DKEK. This is an optional parameter. Using without will disable the DKEK completely. Using 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 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, 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. value Define number of PIN retries for user PIN during initialization. Default is 3. value The hexadecimal AID of of the biometric server for template 1. Switches on the use of the user PIN as session PIN. value The hexadecimal AID of of the biometric server for template 2. Switches on the use of the user PIN as session PIN. value Define password for DKEK share encryption. If set to env:VARIABLE, the value of the environment variable VARIABLE is used. value Define threshold for number of password shares required for reconstruction. value Define number of password shares. Force removal of existing key, description and certificate. label, label Define the token label to be used in --initialize. arg, 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. total-number-of-public-keys, total-number-of-public-keys Define the total number of public keys to use for public key authentication when using . is optional, but if it's present, it must be used with . When the SmartCard-HSM is initialized with these options, it will require M-of-N public key authentication to be used, where sets the M and sets the N. After the initialization, the user should use to register the N public keys before the SmartCard-HSM can be used. required-number-of-public-keys, required-number-of-public-keys Define the required number of public keys to use for public key authentication when using . This is the M in M-of-N public key authentication. See for more information. input-public-key-file, 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 . This can only be used when the SmartCard-HSM has been initialized with and and fewer than N public keys have been registered. Use to check the how many public keys have been registered. output-public-key-file, output-public-key-file Export a public key to be used for M-of-N public key authentication. This should be used with to choose the key to export. The file should be registered on another SmartCard-HSM using . 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 for a card to be inserted , 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 andreas.schwier@cardcontact.de. OpenSC-0.26.1/doc/tools/tools.html000066400000000000000000006215571474147347300167200ustar00rootroot00000000000000OpenSC Manual Pages: Section 1

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
netkey-tool — administrative utility for Netkey E4 cards
npa-tool — displays information on the German eID card (neuer Personalausweis, nPA).
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

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 on the German eID card (neuer Personalausweis, nPA).

Synopsis

npa-tool [OPTIONS]

Description

The npa-tool utility is used to display information stored on the German eID card (neuer Personalausweis, nPA), 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 other identity cards and machine readable travel documents (MRTDs) may be read as well.

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)

Read and write data groups

--read-dg1

Read data group 1: Document Type.

--read-dg2

Read data group 2: Issuing State.

--read-dg3

Read data group 3: Date of Expiry.

--read-dg4

Read data group 4: Given Name(s).

--read-dg5

Read data group 5: Family Name.

--read-dg6

Read data group 6: Religious/Artistic Name.

--read-dg7

Read data group 7: Academic Title.

--read-dg8

Read data group 8: Date of Birth.

--read-dg9

Read data group 9: Place of Birth.

--read-dg10

Read data group 10: Nationality.

--read-dg11

Read data group 11: Sex.

--read-dg12

Read data group 12: Optional Data.

--read-dg13

Read data group 13: Birth Name.

--read-dg14

Read data group 14.

--read-dg15

Read data group 15.

--read-dg16

Read data group 16.

--read-dg17

Read data group 17: Normal Place of Residence.

--read-dg18

Read data group 18: Community ID.

--read-dg19

Read data group 19: Residence Permit I.

--read-dg20

Read data group 20: Residence Permit II.

--read-dg21

Read data group 21: Optional Data.

--write-dg17 HEX_STRING

Write data group 17: Normal Place of Residence.

--write-dg18 HEX_STRING

Write data group 18: Community ID.

--write-dg19 HEX_STRING

Write data group 19: Residence Permit I.

--write-dg20 HEX_STRING

Write data group 20: Residence Permit II.

--write-dg21 HEX_STRING

Write data group 21: Optional Data.

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.

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.

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 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. 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]

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 800-73-3. It is intended for use with test cards only. It can be used to 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

--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:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX: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 or 9E and alg is 06, 07, 11 or 14 for RSA 1024, RSA 2048, ECC 256 or ECC 384 respectively.

--object ContainerID, -O ContainerID

Load an object onto the card. The ContainerID is as defined in NIST 800-73-n without leading 0x. Example: CHUID object is 3000

--cert ref, -C ref

Load a certificate onto the card. ref is 9A, 9C, 9D or 9E

--compresscert ref, -Z ref

Load a certificate that has been gzipped onto the card. ref is 9A, 9C, 9D or 9E

--out file, -o file

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.

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, GOSTR3410-2012-256:B, DES:8, DES3:24, AES:16, AES: 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.

--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.

--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)

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 --applicatin-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. If you are generating an EC key, the curve designation must be specified, for example ec:prime256v1. 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 .

OpenSC-0.26.1/doc/tools/tools.xml000066400000000000000000000021061474147347300165330ustar00rootroot00000000000000 OpenSC Manual Pages: Section 1 OpenSC-0.26.1/doc/tools/westcos-tool.1.xml000066400000000000000000000155641474147347300202100ustar00rootroot00000000000000 westcos-tool 1 OpenSC OpenSC Tools opensc westcos-tool utility for manipulating data structures on westcos smart cards 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 , Changes a PIN stored on the card. User authentication is required for this operation. file, file Write certificate file file in PEM format to the card. User authentication is required for this operation. , 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 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 ). By default the key length is 2048 bits. User authentication is required for this operation. , Print help message on screen. , Install PIN file in on the card. You must provide a PIN value with . length, length Change the length of private key. Use with . , Overwrite the key if there is already a key on the card. pin, pin 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. filename, filename Read the file filename from the card. The file is written on disk with name filename. User authentication is required for this operation. arg, 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. , Unblocks a PIN stored on the card. Knowledge of the PIN Unblock Key (PUK) is required for this operation. Causes westcos-tool to be more verbose. Specify this flag several times to enable debug output in the OpenSC library. , Wait for a card to be inserted. filename, 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 francois.leblanc@cev-sa.com. OpenSC-0.26.1/etc/000077500000000000000000000000001474147347300135205ustar00rootroot00000000000000OpenSC-0.26.1/etc/DESCHSMCVCA00001000066400000000000000000000006751474147347300155570ustar00rootroot00000000000000!Np_)BDESCHSMCVCA00001I  Wۡ>f rn;#& ( HnSw }Z u,0Wu0AzU&\lJKD0ك &\lJKD0ٻ|)\kAҮ~W,KH/'#:DS2bT~5FawE-T\T/i Wۡ>f q9zaHVA@=V V4J{!ɓ20E+WsiS-G^[._ DESCHSMCVCA00001L +S_%_$_7@/a~6`o2S17ǵڑ25*;śLŕǦ8o:K"յlU !@OpenSC-0.26.1/etc/DESRCACC100001000066400000000000000000000006711474147347300152600ustar00rootroot00000000000000!Nl_)BDESRCACC100001I  Wۡ>f rn;#& ( HnSw }Z u,0Wu0AzU&\lJKD0ك &\lJKD0ٻ|)\kAҮ~W,KH/'#:DS2bT~5FawE-T\T/i Wۡ>f q9zaHVAmZ&ͺ$_rtm@:=\kwmrC({e&c+Ѫ1=b_ DESRCACC100001L +S_% _$_7@8+Һ# bgе+UV3=UHí{]x l<.׍@w߉aOpenSC-0.26.1/etc/Makefile.am000066400000000000000000000034441474147347300155610ustar00rootroot00000000000000CV_CERTS = DESRCACC100001 DESCHSMCVCA00001 MAINTAINERCLEANFILES = $(srcdir)/Makefile.in DISTCLEANFILES = opensc.conf.example EXTRA_DIST = $(CV_CERTS) Makefile.mak SUFFIXES = .in 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 required. force: opensc.conf.example: opensc.conf.example.in force .in: $(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' \ -e 's|@DEFAULT_SM_MODULE[@]|$(DEFAULT_SM_MODULE)|g' \ -e 's|@DEFAULT_SM_MODULE_PATH[@]|$(DEFAULT_SM_MODULE_PATH)|g' \ -e 's|@DYN_LIB_EXT[@]|$(DYN_LIB_EXT)|g' \ -e 's|@LIBDIR[@]|$(LIBDIR)|g' \ -e 's|@LIB_PRE[@]|$(LIB_PRE)|g' \ -e 's|@PROFILE_DIR[@]|$(PROFILE_DIR)|g' \ -e 's|@PROFILE_DIR_DEFAULT[@]|$(PROFILE_DIR_DEFAULT)|g' \ < $< > $@ install-exec-hook: opensc.conf.example $(MKDIR_P) "$(DESTDIR)$(sysconfdir)" if [ -f "$(DESTDIR)$(sysconfdir)/opensc.conf" ]; then \ $(INSTALL_DATA) $(srcdir)/opensc.conf "$(DESTDIR)$(sysconfdir)/opensc.conf.new"; \ else \ $(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.example rm -f "$(DESTDIR)$(sysconfdir)/opensc.conf.new" "$(DESTDIR)$(sysconfdir)/opensc.conf" rm -f "$(DESTDIR)$(docdir)/opensc.conf" if ENABLE_OPENPACE install-data-local: $(MKDIR_P) "$(DESTDIR)$(CVCDIR)" for cert in $(CV_CERTS); do $(INSTALL_DATA) $(srcdir)/$${cert} "$(DESTDIR)$(CVCDIR)"; done uninstall-local: for cert in $(CV_CERTS); do rm -f "$(DESTDIR)$(CVCDIR)/$${cert}"; done endif OpenSC-0.26.1/etc/Makefile.mak000066400000000000000000000000141474147347300157220ustar00rootroot00000000000000all clean:: OpenSC-0.26.1/etc/opensc.conf000066400000000000000000000001001474147347300156450ustar00rootroot00000000000000app default { # debug = 3; # debug_file = opensc-debug.txt; } OpenSC-0.26.1/etc/opensc.conf.example.in000066400000000000000000001342731474147347300177270ustar00rootroot00000000000000# 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: ignore in tokend, protect otherwise # 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.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; # Tokend ignore to read PIN protected certificate that is set SC_PKCS15_CO_FLAG_PRIVATE flag. # Default: true # # ignore_private_certificate = false; } } # Used by OpenSC minidriver on Windows only app cardmod { } OpenSC-0.26.1/m4/000077500000000000000000000000001474147347300132655ustar00rootroot00000000000000OpenSC-0.26.1/m4/ax_ac_append_to_file.m4000066400000000000000000000016221474147347300176330ustar00rootroot00000000000000# =========================================================================== # 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" ]) ]) OpenSC-0.26.1/m4/ax_ac_print_to_file.m4000066400000000000000000000016111474147347300175160ustar00rootroot00000000000000# =========================================================================== # 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" ]) ]) OpenSC-0.26.1/m4/ax_add_am_macro_static.m4000066400000000000000000000015251474147347300201570ustar00rootroot00000000000000# =========================================================================== # 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]) ]) OpenSC-0.26.1/m4/ax_am_macros_static.m4000066400000000000000000000021251474147347300175270ustar00rootroot00000000000000# =========================================================================== # 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])[ ]) ]) OpenSC-0.26.1/m4/ax_check_compile_flag.m4000066400000000000000000000064031474147347300200000ustar00rootroot00000000000000# =========================================================================== # 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 OpenSC-0.26.1/m4/ax_check_gnu_make.m4000066400000000000000000000077271474147347300171570ustar00rootroot00000000000000# =========================================================================== # 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]) ]) OpenSC-0.26.1/m4/ax_code_coverage.m4000066400000000000000000000276141474147347300170160ustar00rootroot00000000000000# =========================================================================== # 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 ]) OpenSC-0.26.1/m4/ax_file_escapes.m4000066400000000000000000000013731474147347300166450ustar00rootroot00000000000000# =========================================================================== # 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="\"" ]) OpenSC-0.26.1/m4/ax_pthread.m4000066400000000000000000000540341474147347300156540ustar00rootroot00000000000000# =========================================================================== # https://www.gnu.org/software/autoconf-archive/ax_pthread.html # =========================================================================== # # SYNOPSIS # # AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) # # DESCRIPTION # # This macro figures out how to build C programs using POSIX threads. It # sets the PTHREAD_LIBS output variable to the threads library and linker # flags, and the PTHREAD_CFLAGS output variable to any special C compiler # 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 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 # that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX). # # Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the # PTHREAD_PRIO_INHERIT symbol is defined when compiling with # PTHREAD_CFLAGS. # # ACTION-IF-FOUND is a list of shell commands to run if a threads library # is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it # is not found. If ACTION-IF-FOUND is not specified, the default action # will define HAVE_PTHREAD. # # Please let the authors know if this macro fails on any platform, or if # you have any other suggestions or comments. This macro was based on work # by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help # from M. Frigo), as well as ac_pthread and hb_pthread macros posted by # Alejandro Forero Cuervo to the autoconf macro repository. We are also # grateful for the helpful feedback of numerous users. # # Updated for Autoconf 2.68 by Daniel Richard G. # # LICENSE # # 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 # 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 31 AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) AC_DEFUN([AX_PTHREAD], [ AC_REQUIRE([AC_CANONICAL_HOST]) AC_REQUIRE([AC_PROG_CC]) AC_REQUIRE([AC_PROG_SED]) AC_LANG_PUSH([C]) ax_pthread_ok=no # We used to check for pthread.h first, but this fails if pthread.h # requires special compiler flags (e.g. on Tru64 or Sequent). # It gets checked for in the link test anyway. # First of all, check if the user has set any of the PTHREAD_LIBS, # etcetera environment variables, and if threads linking works using # them: if test "x$PTHREAD_CFLAGS$PTHREAD_LIBS" != "x"; then ax_pthread_save_CC="$CC" 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]) AC_LINK_IFELSE([AC_LANG_CALL([], [pthread_join])], [ax_pthread_ok=yes]) AC_MSG_RESULT([$ax_pthread_ok]) if test "x$ax_pthread_ok" = "xno"; then PTHREAD_LIBS="" PTHREAD_CFLAGS="" fi CC="$ax_pthread_save_CC" CFLAGS="$ax_pthread_save_CFLAGS" LIBS="$ax_pthread_save_LIBS" fi # We must check for the threads library under a number of different # names; the ordering is very important because some systems # (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 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" # The ordering *is* (sometimes) important. Some notes on the # individual items follow: # pthreads: AIX (must check this before -lpthread) # none: in case threads are in libc; should be tried before -Kthread and # other compiler flags to prevent continual compiler warnings # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) # -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads), Tru64 # (Note: HP C rejects this with "bad form for `-t' option") # -pthreads: Solaris/gcc (Note: HP C also rejects) # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it # doesn't hurt to check since this sometimes defines pthreads and # -D_REENTRANT too), HP C (must be checked before -lpthread, which # is present but should not be used directly; and before -mthreads, # because the compiler interprets this as "-mt" + "-hreads") # -mthreads: Mingw32/gcc, Lynx/gcc # pthread: Linux, etcetera # --thread-safe: KAI C++ # pthread-config: use pthread-config program (for GNU Pth library) case $host_os in freebsd*) # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) ax_pthread_flags="-kthread lthread $ax_pthread_flags" ;; hpux*) # From the cc(1) man page: "[-mt] Sets various -D flags to enable # multi-threading and also sets -lpthread." ax_pthread_flags="-mt -pthread pthread $ax_pthread_flags" ;; openedition*) # IBM z/OS requires a feature-test macro to be defined in order to # enable POSIX threads at all, so give the user a hint if this is # not set. (We don't define these ourselves, as they can affect # other portions of the system API in unpredictable ways.) AC_EGREP_CPP([AX_PTHREAD_ZOS_MISSING], [ # if !defined(_OPEN_THREADS) && !defined(_UNIX03_THREADS) AX_PTHREAD_ZOS_MISSING # endif ], [AC_MSG_WARN([IBM z/OS requires -D_OPEN_THREADS or -D_UNIX03_THREADS to enable pthreads support.])]) ;; solaris*) # On Solaris (at least, for some versions), libc contains stubbed # (non-functional) versions of the pthreads routines, so link-based # tests will erroneously succeed. (N.B.: The stubs are missing # pthread_cleanup_push, or rather a function called by this macro, # so we could check for that, but who knows whether they'll stub # 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,-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,-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 # correctly enabled case $host_os in darwin* | hpux* | linux* | osf* | solaris*) ax_pthread_check_macro="_REENTRANT" ;; aix*) ax_pthread_check_macro="_THREAD_SAFE" ;; *) ax_pthread_check_macro="--" ;; esac AS_IF([test "x$ax_pthread_check_macro" = "x--"], [ax_pthread_check_cond=0], [ax_pthread_check_cond="!defined($ax_pthread_check_macro)"]) 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 # Clang needs special handling, because older versions handle the -pthread # option in a rather... idiosyncratic way if test "x$ax_pthread_clang" = "xyes"; then # Clang takes -pthread; it has never supported any other flag # (Note 1: This will need to be revisited if a system that Clang # supports has POSIX threads in a separate library. This tends not # to be the way of modern systems, but it's conceivable.) # (Note 2: On some systems, notably Darwin, -pthread is not needed # to get POSIX threads support; the API is always present and # active. We could reasonably leave PTHREAD_CFLAGS empty. But # -pthread does define _REENTRANT, and while the Darwin headers # ignore this macro, third-party headers might not.) # 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 # during compilation"). They expect -pthread to be passed in only # when source code is being compiled. # # Problem is, this is at odds with the way Automake and most other # C build frameworks function, which is that the same flags used in # compilation (CFLAGS) are also used in linking. Many systems # supported by AX_PTHREAD require exactly this for POSIX threads # support, and in fact it is often not straightforward to specify a # flag that is used only in the compilation phase and not in # linking. Such a scenario is extremely rare in practice. # # Even though use of the -pthread flag in linking would only print # a warning, this can be a nuisance for well-run software projects # that build with -Werror. So if the active version of Clang has # this misfeature, we search for an option to squash it. AC_CACHE_CHECK([whether Clang needs flag to prevent "argument unused" warning when linking with -pthread], [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG], [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG=unknown # Create an alternate version of $ac_link that compiles and # links in two steps (.c -> .o, .o -> exe) instead of one # (.c -> exe), because the warning occurs only in the second # 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_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 AS_IF([test "x$ax_pthread_try" = "xunknown"], [break]) CFLAGS="-Werror -Wunknown-warning-option $ax_pthread_try -pthread $ax_pthread_save_CFLAGS" ac_link="$ax_pthread_save_ac_link" AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])], [ac_link="$ax_pthread_2step_ac_link" AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])], [break]) ]) done ac_link="$ax_pthread_save_ac_link" CFLAGS="$ax_pthread_save_CFLAGS" AS_IF([test "x$ax_pthread_try" = "x"], [ax_pthread_try=no]) ax_cv_PTHREAD_CLANG_NO_WARN_FLAG="$ax_pthread_try" ]) case "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" in no | unknown) ;; *) PTHREAD_CFLAGS="$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG $PTHREAD_CFLAGS" ;; esac fi # $ax_pthread_clang = yes # Various other checks: if test "x$ax_pthread_ok" = "xyes"; then ax_pthread_save_CFLAGS="$CFLAGS" ax_pthread_save_LIBS="$LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. AC_CACHE_CHECK([for joinable pthread attribute], [ax_cv_PTHREAD_JOINABLE_ATTR], [ax_cv_PTHREAD_JOINABLE_ATTR=unknown for ax_pthread_attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ], [int attr = $ax_pthread_attr; return attr /* ; */])], [ax_cv_PTHREAD_JOINABLE_ATTR=$ax_pthread_attr; break], []) done ]) AS_IF([test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xunknown" && \ test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xPTHREAD_CREATE_JOINABLE" && \ test "x$ax_pthread_joinable_attr_defined" != "xyes"], [AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE], [$ax_cv_PTHREAD_JOINABLE_ATTR], [Define to necessary symbol if this constant uses a non-standard name on your system.]) ax_pthread_joinable_attr_defined=yes ]) AC_CACHE_CHECK([whether more special flags are required for pthreads], [ax_cv_PTHREAD_SPECIAL_FLAGS], [ax_cv_PTHREAD_SPECIAL_FLAGS=no case $host_os in solaris*) ax_cv_PTHREAD_SPECIAL_FLAGS="-D_POSIX_PTHREAD_SEMANTICS" ;; esac ]) AS_IF([test "x$ax_cv_PTHREAD_SPECIAL_FLAGS" != "xno" && \ test "x$ax_pthread_special_flags_added" != "xyes"], [PTHREAD_CFLAGS="$ax_cv_PTHREAD_SPECIAL_FLAGS $PTHREAD_CFLAGS" ax_pthread_special_flags_added=yes]) AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT], [ax_cv_PTHREAD_PRIO_INHERIT], [AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], [[int i = PTHREAD_PRIO_INHERIT; return i;]])], [ax_cv_PTHREAD_PRIO_INHERIT=yes], [ax_cv_PTHREAD_PRIO_INHERIT=no]) ]) AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes" && \ test "x$ax_pthread_prio_inherit_defined" != "xyes"], [AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.]) ax_pthread_prio_inherit_defined=yes ]) CFLAGS="$ax_pthread_save_CFLAGS" LIBS="$ax_pthread_save_LIBS" # More AIX lossage: compile with *_r variant if test "x$GCC" != "xyes"; then case $host_os in aix*) AS_CASE(["x/$CC"], [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6], [#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"]) 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 ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1]) : else ax_pthread_ok=no $2 fi AC_LANG_POP ])dnl AX_PTHREAD OpenSC-0.26.1/m4/ax_valgrind_check.m4000066400000000000000000000213531474147347300171660ustar00rootroot00000000000000# =========================================================================== # 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])]) ]) OpenSC-0.26.1/m4/m4_ax_func_getopt_long.m4000066400000000000000000000053251474147347300201600ustar00rootroot00000000000000# =========================================================================== # 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])])])]) OpenSC-0.26.1/m4/version.m4000066400000000000000000000003361474147347300152160ustar00rootroot00000000000000dnl version.m4 - source for version.m4.ci generated by bootstrap.ci dnl * bootstrap.ci generates version.m4.ci based on this file dnl * if version.m4.ci exists, it is included in configure.ac define([PACKAGE_SUFFIX], []) OpenSC-0.26.1/packaging/000077500000000000000000000000001474147347300146715ustar00rootroot00000000000000OpenSC-0.26.1/packaging/debian.templates/000077500000000000000000000000001474147347300201105ustar00rootroot00000000000000OpenSC-0.26.1/packaging/debian.templates/changelog000066400000000000000000000001531474147347300217610ustar00rootroot00000000000000#PACKAGE# (#VERSION#-1) unstable; urgency=low * New upstream release. -- #USERNAME# <#EMAIL#> #DATE# OpenSC-0.26.1/packaging/debian.templates/compat000066400000000000000000000000021474147347300213060ustar00rootroot000000000000008 OpenSC-0.26.1/packaging/debian.templates/control000066400000000000000000000030131474147347300215100ustar00rootroot00000000000000Source: opensc Section: utils Priority: extra Maintainer: Viktor Tarasov Build-Depends: debhelper (>= 8.0.0), autotools-dev, libltdl3-dev, libreadline-dev, libssl-dev (>= 0.9.7d-3), libpcsclite-dev (>= 1.2.9-beta1), pkg-config, xsltproc, docbook-xsl, zlib1g-dev, dh-autoreconf, flex Standards-Version: 3.9.2 Homepage: https://github.com/OpenSC/OpenSC #Vcs-Git: git://git.debian.org/collab-maint/opensc.git #Vcs-Browser: http://git.debian.org/?p=collab-maint/opensc.git;a=summary Package: opensc Section: utils Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, pcscd Replaces: libopensc2 (<< 0.12.0) Conflicts: libopensc2 (<< 0.12.0), mozilla-opensc Description: Smart card utilities with support for PKCS#15 compatible cards OpenSC provides a set of libraries and utilities to access smart cards. It mainly focuses on cards that support cryptographic operations. It facilitates their use in security applications such as mail encryption, authentication, and digital signature. OpenSC implements the PKCS#11 API. OpenSC implements the PKCS#15 standard and aims to be compatible with all software that does so as well. . Before purchasing any cards, please read carefully documentation in /usr/share/doc/opensc/html/wiki/index.html - only some cards are supported. Not only does card type matters, but also card version, card OS version and preloaded applet. Only a subset of possible operations may be supported for your card. Card initialization may require third party proprietary software. OpenSC-0.26.1/packaging/debian.templates/copyright000066400000000000000000000021551474147347300220460ustar00rootroot00000000000000Format: http://dep.debian.net/deps/dep5 Upstream-Name: opensc Source: https://github.com/OpenSC/OpenSC.git Files: * Copyright: 2001-2013 OpenSC developers License: LGPL-2.1 License: LGPL-2.1 This package 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 3 of the License, or (at your option) any later version. . This package 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 . . On Debian systems, the complete text of the GNU Lesser General Public License can be found in "/usr/share/common-licenses/LGPL-3". # Please also look if there are files or directories which have a # different copyright/license attached and list them here. OpenSC-0.26.1/packaging/debian.templates/docs000066400000000000000000000000141474147347300207560ustar00rootroot00000000000000NEWS README OpenSC-0.26.1/packaging/debian.templates/opensc.install000066400000000000000000000005141474147347300227670ustar00rootroot00000000000000debian/tmp/usr/bin/pkcs15-* debian/tmp/usr/bin/*-tool debian/tmp/usr/bin/eidenv debian/tmp/usr/bin/opensc-explorer debian/tmp/usr/lib/*.so.* debian/tmp/usr/lib/*.so debian/tmp/usr/lib/pkcs11/*.so debian/tmp/usr/share/opensc/*.profile etc/opensc.conf etc/opensc debian/tmp/usr/share/man/man5/*.5 debian/tmp/usr/share/man/man1/*.1 OpenSC-0.26.1/packaging/debian.templates/rules000077500000000000000000000014661474147347300211770ustar00rootroot00000000000000#!/usr/bin/make -f # -*- makefile -*- # Sample debian/rules that uses debhelper. # This file was originally written by Joey Hess and Craig Small. # As a special exception, when this file is copied by dh-make into a # dh-make output file, you may use that output file without restriction. # This special exception was added by Craig Small in version 0.37 of dh-make. # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 %: dh $@ --with autoreconf override_dh_auto_configure: dh_auto_configure -- --sysconfdir=/etc/opensc \ --enable-pcsc \ --disable-openct \ --enable-doc \ --enable-readline \ --enable-zlib \ --enable-sm \ --htmldir=/usr/share/doc/opensc/html override_dh_auto_install: dh_auto_install --destdir=debian/tmp override_dh_installdocs: dh_installdocs -A README NEWS ChangeLog OpenSC-0.26.1/packaging/opensc.module000066400000000000000000000006401474147347300173670ustar00rootroot00000000000000# 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.so OpenSC-0.26.1/packaging/opensc.spec000066400000000000000000000155321474147347300170420ustar00rootroot00000000000000Name: opensc Version: 0.1.0 Release: 1%{?dist} Summary: Smart card library and applications License: LGPL-2.1-or-later AND BSD-3-Clause URL: https://github.com/OpenSC/OpenSC/wiki Source0: opensc-0.1.0.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} > 40 || 0%{?rhel} > 10 BuildRequires: bash-completion-devel %else BuildRequires: bash-completion %endif BuildRequires: zlib-ng-devel # For tests BuildRequires: libcmocka-devel BuildRequires: vim-common %if ! 0%{?rhel} BuildRequires: softhsm BuildRequires: openssl BuildRequires: openpace-devel %endif 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 # The test-pkcs11-tool-allowed-mechanisms already works in Fedora 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 %{_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}/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 OpenSC-0.26.1/src/000077500000000000000000000000001474147347300135345ustar00rootroot00000000000000OpenSC-0.26.1/src/Makefile.am000066400000000000000000000003711474147347300155710ustar00rootroot00000000000000MAINTAINERCLEANFILES = $(srcdir)/Makefile.in EXTRA_DIST = Makefile.mak # Order IS important SUBDIRS = common scconf ui pkcs15init sm \ libopensc pkcs11 tools minidriver if ENABLE_TESTS SUBDIRS += tests endif if ENABLE_SM SUBDIRS += smm endif OpenSC-0.26.1/src/Makefile.mak000066400000000000000000000011511474147347300157410ustar00rootroot00000000000000TOPDIR = .. SUBDIRS = common scconf ui sm pkcs15init \ libopensc pkcs11 tools default: all !INCLUDE $(TOPDIR)\win32\Make.rules.mak !IF "$(MINIDRIVER_DEF)" == "/DENABLE_MINIDRIVER" SUBDIRS = $(SUBDIRS) minidriver !ENDIF !IF "$(SM_DEF)" == "/DENABLE_SM" SUBDIRS = $(SUBDIRS) smm !ENDIF !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 OpenSC-0.26.1/src/common/000077500000000000000000000000001474147347300150245ustar00rootroot00000000000000OpenSC-0.26.1/src/common/ChangeLog.compat_getopt000066400000000000000000000010661474147347300214450ustar00rootroot000000000000002002-07-26 Benjamin C. W. Sittler * README: updated for version 1.4 * my_getopt.c: now we include explicitly for those systems that narrowly (mis-)interpret ANSI C and POSIX (_my_getopt_internal): added an explicit cast to size_t to make g++ happy * getopt.h, my_getopt.h: added extern "C" { ... } for C++ compilation (thanks to Jeff Lawson and others) 2001-08-20 Benjamin C. W. Sittler * getopt.h (getopt_long_only): fixed typo (thanks to Justin Lee ) OpenSC-0.26.1/src/common/LICENSE.compat_getopt000066400000000000000000000021261474147347300206760ustar00rootroot00000000000000compat_getopt - a command-line argument parser Copyright 1997-2001, Benjamin Sittler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. OpenSC-0.26.1/src/common/Makefile.am000066400000000000000000000031251474147347300170610ustar00rootroot00000000000000MAINTAINERCLEANFILES = $(srcdir)/Makefile.in EXTRA_DIST = Makefile.mak noinst_LTLIBRARIES = libcompat.la libpkcs11.la libscdl.la noinst_PROGRAMS = compat_getopt_main dist_noinst_DATA = \ README.compat_getopt ChangeLog.compat_getopt \ 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 \ compat_strnlen.h compat_strnlen.c \ compat_getpass.h compat_getpass.c \ compat_getopt.h compat_getopt.c \ compat_report_rangecheckfailure.c \ compat___iob_func.c \ compat_overflow.h compat_overflow.c \ simclist.c simclist.h \ libpkcs11.c libscdl.c \ constant-time.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 OpenSC-0.26.1/src/common/Makefile.mak000066400000000000000000000010051474147347300172270ustar00rootroot00000000000000TOPDIR = ..\.. 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 !INCLUDE $(TOPDIR)\win32\Make.rules.mak common.lib: $(COMMON_OBJECTS) lib $(LIBFLAGS) /out:common.lib $(COMMON_OBJECTS) libpkcs11.lib: libpkcs11.obj lib $(LIBFLAGS) /out:libpkcs11.lib libpkcs11.obj libscdl.lib: libscdl.obj lib $(LIBFLAGS) /out:libscdl.lib libscdl.obj OpenSC-0.26.1/src/common/README.compat_getopt000066400000000000000000000125551474147347300205600ustar00rootroot00000000000000my_getopt - a command-line argument parser Copyright 1997-2002, Benjamin Sittler The author can be reached by sending email to . The version of my_getopt in this package (1.4) has a BSD-like license; see the file LICENSE for details. Version 1.0 of my_getopt was similar to the GPL'ed version of my_getopt included with SMOKE-16 Version 1, Release 19990717. SMOKE-16 packages are available from: http://geocities.com/bsittler/#smoke16 OVERVIEW OF THE ARGUMENT PARSER =============================== The getopt(), getopt_long() and getopt_long_only() functions parse command line arguments. The argc and argv parameters passed to these functions correspond to the argument count and argument list passed to your program's main() function at program start-up. Element 0 of the argument list conventionally contains the name of your program. Any remaining arguments starting with "-" (except for "-" or "--" by themselves) are option arguments, some of include option values. This family of getopt() functions allows intermixed option and non-option arguments anywhere in the argument list, except that "--" by itself causes the remaining elements of the argument list to be treated as non-option arguments. [ See the parts of this document labeled "DOCUMENTATION" and "WHY RE-INVENT THE WHEEL?" for a more information. ] FILES ===== The following four files constitute the my_getopt package: LICENSE - license and warranty information for my_getopt my_getopt.c - implementation of my getopt replacement my_getopt.h - interface for my getopt replacement getopt.h - a header file to make my getopt look like GNU getopt USAGE ===== To use my_getopt in your application, include the following line to your main program source: #include "getopt.h" This line should appear after your standard system header files to avoid conflicting with your system's built-in getopt. Then compile my_getopt.c into my_getopt.o, and link my_getopt.o into your application: $ cc -c my_getopt.c $ ld -o app app.o ... my_getopt.o To avoid conflicting with standard library functions, the function names and global variables used by my_getopt all begin with `my_'. To ensure compatibility with existing C programs, the `getopt.h' header file uses the C preprocessor to redefine names like getopt, optarg, optind, and so forth to my_getopt, my_optarg, my_optind, etc. SAMPLE PROGRAM ============== There is also a public-domain sample program: main.c - main() for a sample program using my_getopt Makefile - build script for the sample program (called `copy') To build and test the sample program: $ make $ ./copy -help $ ./copy -version The sample program bears a slight resemblance to the UNIX `cat' utility, but can be used rot13-encode streams, and can redirect output to a file. DOCUMENTATION ============= There is not yet any real documentation for my_getopt. For the moment, use the Linux manual page for getopt. It has its own copyright and license; view the file `getopt.3' in a text editor for more details. getopt.3 - the manual page for GNU getopt getopt.txt - preformatted copy of the manual page for GNU getopt, for your convenience WHY RE-INVENT THE WHEEL? ======================== I re-implemented getopt, getopt_long, and getopt_long_only because 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 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 parsing. A required long option argument can be in the same word as the option name, separated by '=', or in the next word. An optional long option argument must be in the same word as the option name, separated by '='. As with the GNU versions, a '+' prefix to the short option specification (or the POSIXLY_CORRECT environment variable) disables 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 receive ':' instead of '?' for missing option arguments, prefix the short option specification with ':'. The original intent was to re-implement the documented behavior of the GNU versions, but I have found it necessary to emulate some of the undocumented behavior as well. Some programs depend on it. KNOWN BUGS ========== The GNU versions support POSIX-style -W "name=value" long options. Currently, my_getopt does not support these, because I don't have any documentation on them (other than the fact that they are enabled by "W;" in the short option specification.) As a temporary workaround, my_getopt treats "W;" in the short option string identically to "W:". The GNU versions support internationalized/localized messages. Currently, my_getopt does not. There should be re-entrant versions of all these functions so that multiple threads can parse arguments simultaneously. OpenSC-0.26.1/src/common/README.compat_strlcpy000066400000000000000000000005271474147347300207520ustar00rootroot00000000000000strncpy() is unsafe since it does not always add a final NUL-byte. OpenBSD developed a safer version called strlcpy(). Use "man -l strlcpy.3" to read the manpage. The files strlcpy.3 and strlcpy.c comes from ftp://ftp.openbsd.org/pub/OpenBSD/src/lib/libc/string/ and are Copyright (c) 1998, 2000 Todd C. Miller OpenSC-0.26.1/src/common/compat___iob_func.c000066400000000000000000000014341474147347300206170ustar00rootroot00000000000000#ifdef HAVE_CONFIG_H #include "config.h" #endif #ifndef HAVE_IOB_FUNC /* empty file if iob_func is available */ #ifdef _WIN32 #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; return my_iob; } #endif #endif #endif OpenSC-0.26.1/src/common/compat_dummy.c000066400000000000000000000001621474147347300176650ustar00rootroot00000000000000/* Required so ld will not complain regarding empty lib */ void compat_dummy (void); void compat_dummy (void) { } OpenSC-0.26.1/src/common/compat_getopt.3000066400000000000000000000223621474147347300177620ustar00rootroot00000000000000.\" (c) 1993 by Thomas Koenig (ig25@rz.uni-karlsruhe.de) .\" .\" Permission is granted to make and distribute verbatim copies of this .\" manual provided the copyright notice and this permission notice are .\" preserved on all copies. .\" .\" Permission is granted to copy and distribute modified versions of this .\" manual under the conditions for verbatim copying, provided that the .\" entire resulting derived work is distributed under the terms of a .\" permission notice identical to this one .\" .\" Since the Linux kernel and libraries are constantly changing, this .\" manual page may be incorrect or out-of-date. The author(s) assume no .\" responsibility for errors or omissions, or for damages resulting from .\" the use of the information contained herein. The author(s) may not .\" have taken the same level of care in the production of this manual, .\" which is licensed free of charge, as they might when working .\" professionally. .\" .\" Formatted or processed versions of this manual, if unaccompanied by .\" the source, must acknowledge the copyright and authors of this work. .\" License. .\" Modified Sat Jul 24 19:27:50 1993 by Rik Faith (faith@cs.unc.edu) .\" Modified Mon Aug 30 22:02:34 1995 by Jim Van Zandt .\" longindex is a pointer, has_arg can take 3 values, using consistent .\" names for optstring and longindex, "\n" in formats fixed. Documenting .\" opterr and getopt_long_only. Clarified explanations (borrowing heavily .\" from the source code). .TH GETOPT 3 "Aug 30, 1995" "GNU" "Linux Programmer's Manual" .SH NAME getopt \- Parse command line options .SH SYNOPSIS .nf .B #include .sp .BI "int getopt(int " argc ", char * const " argv[] "," .BI " const char *" optstring ");" .sp .BI "extern char *" optarg ; .BI "extern int " optind ", " opterr ", " optopt ; .sp .B #include .sp .BI "int getopt_long(int " argc ", char * const " argv[] ", .BI " const char *" optstring , .BI " const struct option *" longopts ", int *" longindex ");" .sp .BI "int getopt_long_only(int " argc ", char * const " argv[] ", .BI " const char *" optstring , .BI " const struct option *" longopts ", int *" longindex ");" .fi .SH DESCRIPTION The .B getopt() function parses the command line arguments. Its arguments .I argc and .I argv are the argument count and array as passed to the .B main() function on program invocation. An element of \fIargv\fP that starts with `-' (and is not exactly "-" or "--") is an option element. The characters of this element (aside from the initial `-') are option characters. If \fBgetopt()\fP is called repeatedly, it returns successively each of the option characters from each of the option elements. .PP If \fBgetopt()\fP finds another option character, it returns that character, updating the external variable \fIoptind\fP and a static variable \fInextchar\fP so that the next call to \fBgetopt()\fP can resume the scan with the following option character or \fIargv\fP-element. .PP If there are no more option characters, \fBgetopt()\fP returns \fBEOF\fP. Then \fIoptind\fP is the index in \fIargv\fP of the first \fIargv\fP-element that is not an option. .PP .I optstring is a string containing the legitimate option characters. If such a character is followed by a colon, the option requires an argument, so \fBgetopt\fP places a pointer to the following text in the same \fIargv\fP-element, or the text of the following \fIargv\fP-element, in .IR optarg . Two colons mean an option takes an optional arg; if there is text in the current \fIargv\fP-element, it is returned in \fIoptarg\fP, otherwise \fIoptarg\fP is set to zero. .PP By default, \fBgetargs()\fP permutes the contents of \fIargv\fP as it scans, so that eventually all the non-options are at the end. Two other modes are also implemented. If the first character of \fIoptstring\fP is `+' or the environment variable POSIXLY_CORRECT is set, then option processing stops as soon as a non-option argument is encountered. If the first character of \fIoptstring\fP is `-', then each non-option \fIargv\fP-element is handled as if it were the argument of an option with character code 1. (This is used by programs that were written to expect options and other \fIargv\fP-elements in any order and that care about the ordering of the two.) The special argument `--' forces an end of option-scanning regardless of the scanning mode. .PP If \fBgetopt()\fP does not recognize an option character, it prints an error message to stderr, stores the character in \fIoptopt\fP, and returns `?'. The calling program may prevent the error message by setting \fIopterr\fP to 0. .PP The .B getopt_long() function works like .B getopt() except that it also accepts long options, started out by two dashes. Long option names may be abbreviated if the abbreviation is unique or is an exact match for some defined option. A long option may take a parameter, of the form .B --arg=param or .BR "--arg param" . .PP .I longopts is a pointer to the first element of an array of .B struct option declared in .B as .nf .sp .in 10 struct option { .in 14 const char *name; int has_arg; int *flag; int val; .in 10 }; .fi .PP The meanings of the different fields are: .TP .I name is the name of the long option. .TP .I has_arg is: \fBno_argument\fP (or 0) if the option does not take an argument, \fBrequired_argument\fP (or 1) if the option requires an argument, or \fBoptional_argument\fP (or 2) if the option takes an optional argument. .TP .I flag specifies how results are returned for a long option. If \fIflag\fP is \fBNULL\fP, then \fBgetopt_long()\fP returns \fIval\fP. (For example, the calling program may set \fIval\fP to the equivalent short option character.) Otherwise, \fBgetopt_long()\fP returns 0, and \fIflag\fP points to a variable which is set to \fIval\fP if the option is found, but left unchanged if the option is not found. .TP \fIval\fP is the value to return, or to load into the variable pointed to by \fIflag\fP. .PP The last element of the array has to be filled with zeroes. .PP If \fIlongindex\fP is not \fBNULL\fP, it points to a variable which is set to the index of the long option relative to .IR longopts . .PP \fBgetopt_long_only()\fP is like \fBgetopt_long()\fP, but `-' as well as `--' can indicate a long option. If an option that starts with `-' (not `--') doesn't match a long option, but does match a short option, it is parsed as a short option instead. .SH "RETURN VALUE" The .B getopt() function returns the option character if the option was found successfully, `:' if there was a missing parameter for one of the options, `?' for an unknown option character, or \fBEOF\fP for the end of the option list. .PP \fBgetopt_long()\fP and \fBgetopt_long_only()\fP also return the option character when a short option is recognized. For a long option, they return \fIval\fP if \fIflag\fP is \fBNULL\fP, and 0 otherwise. Error and EOF returns are the same as for \fBgetopt()\fP, plus `?' for an ambiguous match or an extraneous parameter. .SH "ENVIRONMENT VARIABLES" .TP .SM .B POSIXLY_CORRECT If this is set, then option processing stops as soon as a non-option argument is encountered. .SH "EXAMPLE" The following example program, from the source code, illustrates the use of .BR getopt_long() with most of its features. .nf .sp #include int main (argc, argv) int argc; char **argv; { int c; int digit_optind = 0; while (1) { int this_option_optind = optind ? optind : 1; int option_index = 0; static struct option long_options[] = { {"add", 1, 0, 0}, {"append", 0, 0, 0}, {"delete", 1, 0, 0}, {"verbose", 0, 0, 0}, {"create", 1, 0, 'c'}, {"file", 1, 0, 0}, {0, 0, 0, 0} }; c = getopt_long (argc, argv, "abc:d:012", long_options, &option_index); if (c == -1) break; switch (c) { case 0: printf ("option %s", long_options[option_index].name); if (optarg) printf (" with arg %s", optarg); printf ("\\n"); break; case '0': case '1': case '2': if (digit_optind != 0 && digit_optind != this_option_optind) printf ("digits occur in two different argv-elements.\\n"); digit_optind = this_option_optind; printf ("option %c\\n", c); break; case 'a': printf ("option a\\n"); break; case 'b': printf ("option b\\n"); break; case 'c': printf ("option c with value `%s'\\n", optarg); break; case 'd': printf ("option d with value `%s'\\n", optarg); break; case '?': break; default: printf ("?? getopt returned character code 0%o ??\\n", c); } } if (optind < argc) { printf ("non-option ARGV-elements: "); while (optind < argc) printf ("%s ", argv[optind++]); printf ("\\n"); } exit (0); } .fi .SH "BUGS" This manpage is confusing. .SH "CONFORMING TO" .TP \fBgetopt()\fP: POSIX.1, provided the environment variable POSIXLY_CORRECT is set. Otherwise, the elements of \fIargv\fP aren't really const, because we permute them. We pretend they're const in the prototype to be compatible with other systems. OpenSC-0.26.1/src/common/compat_getopt.c000066400000000000000000000215601474147347300200410ustar00rootroot00000000000000/* * my_getopt.c - my re-implementation of getopt. * Copyright 1997, 2000, 2001, 2002, Benjamin Sittler * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #if ! ( defined(HAVE_GETOPT_H) && defined(HAVE_GETOPT_LONG) && defined(HAVE_GETOPT_LONG_ONLY) ) #include #include #include #include #include "compat_getopt.h" int my_optind=1, my_opterr=1, my_optopt=0; 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 permutation and optional arguments */ int my_getopt(int argc, char * argv[], const char *opts) { static int charind=0; const char *s; char mode, colon_mode; int off = 0, opt = -1; if(getenv("POSIXLY_CORRECT")) colon_mode = mode = '+'; else { if((colon_mode = *opts) == ':') off ++; if(((mode = opts[off]) == '+') || (mode == '-')) { off++; if((colon_mode != ':') && ((colon_mode = opts[off]) == ':')) off ++; } } my_optarg = 0; if(charind) { my_optopt = argv[my_optind][charind]; for(s=opts+off; *s; s++) if(my_optopt == *s) { charind++; if((*(++s) == ':') || ((my_optopt == 'W') && (*s == ';'))) { if(argv[my_optind][charind]) { my_optarg = &(argv[my_optind++][charind]); charind = 0; } else if(*(++s) != ':') { charind = 0; if(++my_optind >= argc) { if(my_opterr) fprintf(stderr, "%s: option requires an argument -- %c\n", argv[0], my_optopt); opt = (colon_mode == ':') ? ':' : '?'; goto my_getopt_ok; } my_optarg = argv[my_optind++]; } } opt = my_optopt; goto my_getopt_ok; } if(my_opterr) fprintf(stderr, "%s: illegal option -- %c\n", argv[0], my_optopt); opt = '?'; if(argv[my_optind][++charind] == '\0') { my_optind++; charind = 0; } my_getopt_ok: if(charind && ! argv[my_optind][charind]) { my_optind++; charind = 0; } } else if((my_optind >= argc) || ((argv[my_optind][0] == '-') && (argv[my_optind][1] == '-') && (argv[my_optind][2] == '\0'))) { my_optind++; opt = -1; } else if((argv[my_optind][0] != '-') || (argv[my_optind][1] == '\0')) { char *tmp; int i, j, k; if(mode == '+') opt = -1; else if(mode == '-') { my_optarg = argv[my_optind++]; charind = 0; opt = 1; } else { for(i=j=my_optind; i j) { tmp=argv[--i]; for(k=i; k+1 argc) my_optind = argc; return opt; } /* this is the extended getopt_long{,_only}, with some GNU-like * extensions. Implements _getopt_internal in case any programs * expecting GNU libc getopt call it. */ int _my_getopt_internal(int argc, char * argv[], const char *shortopts, const struct option *longopts, int *longind, int long_only) { char mode, colon_mode; int shortoff = 0, opt = -1; if(getenv("POSIXLY_CORRECT")) colon_mode = mode = '+'; else { if((colon_mode = *shortopts) == ':') shortoff ++; if(((mode = shortopts[shortoff]) == '+') || (mode == '-')) { shortoff++; if((colon_mode != ':') && ((colon_mode = shortopts[shortoff]) == ':')) shortoff ++; } } my_optarg = 0; if((my_optind >= argc) || ((argv[my_optind][0] == '-') && (argv[my_optind][1] == '-') && (argv[my_optind][2] == '\0'))) { my_optind++; opt = -1; } else if((argv[my_optind][0] != '-') || (argv[my_optind][1] == '\0')) { char *tmp; int i, j, k; opt = -1; if(mode == '+') return -1; else if(mode == '-') { my_optarg = argv[my_optind++]; return 1; } for(i=j=my_optind; i j) { tmp=argv[--i]; for(k=i; k+1= argc) { opt = (colon_mode == ':') ? ':' : '?'; if(my_opterr) fprintf(stderr, "%s: option `--%s' requires an argument\n", argv[0], longopts[found].name); } else my_optarg = argv[my_optind]; } if(!opt) { if (longind) *longind = found; if(!longopts[found].flag) opt = longopts[found].val; else *(longopts[found].flag) = longopts[found].val; } my_optind++; } else if(!hits) { if(offset == 1) opt = my_getopt(argc, argv, shortopts); else { opt = '?'; if(my_opterr) fprintf(stderr, "%s: unrecognized option `%s'\n", argv[0], argv[my_optind++]); } } else { opt = '?'; if(my_opterr) fprintf(stderr, "%s: option `%s' is ambiguous\n", argv[0], argv[my_optind++]); } } if (my_optind > argc) my_optind = argc; return opt; } int my_getopt_long(int argc, char * argv[], const char *shortopts, const struct option *longopts, int *longind) { return _my_getopt_internal(argc, argv, shortopts, longopts, longind, 0); } int my_getopt_long_only(int argc, char * argv[], const char *shortopts, const struct option *longopts, int *longind) { return _my_getopt_internal(argc, argv, shortopts, longopts, longind, 1); } #endif OpenSC-0.26.1/src/common/compat_getopt.h000066400000000000000000000053061474147347300200460ustar00rootroot00000000000000/* * my_getopt.h - interface to my re-implementation of getopt. * Copyright 1997, 2000, 2001, 2002, Benjamin Sittler * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #ifndef MY_GETOPT_H_INCLUDED #define MY_GETOPT_H_INCLUDED #ifdef HAVE_CONFIG_H #include "config.h" #endif /* Prevent mingw32 from including an incompatible getopt implementation */ #define __GETOPT_H__ #ifdef __cplusplus extern "C" { #endif #define getopt my_getopt #define getopt_long my_getopt_long #define getopt_long_only my_getopt_long_only #define _getopt_internal _my_getopt_internal #define opterr my_opterr #define optind my_optind #define optopt my_optopt #define optarg my_optarg /* UNIX-style short-argument parser */ extern int my_getopt(int argc, char * argv[], const char *opts); extern int my_optind, my_opterr, my_optopt; extern char *my_optarg; struct option { const char *name; int has_arg; int *flag; int val; }; /* human-readable values for has_arg */ #undef no_argument #define no_argument 0 #undef required_argument #define required_argument 1 #undef optional_argument #define optional_argument 2 /* GNU-style long-argument parsers */ extern int my_getopt_long(int argc, char * argv[], const char *shortopts, const struct option *longopts, int *longind); extern int my_getopt_long_only(int argc, char * argv[], const char *shortopts, const struct option *longopts, int *longind); extern int _my_getopt_internal(int argc, char * argv[], const char *shortopts, const struct option *longopts, int *longind, int long_only); #ifdef __cplusplus } #endif #endif /* MY_GETOPT_H_INCLUDED */ OpenSC-0.26.1/src/common/compat_getopt.txt000066400000000000000000000226301474147347300204350ustar00rootroot00000000000000 GETOPT(3) Linux Programmer's Manual GETOPT(3) NAME getopt - Parse command line options SYNOPSIS #include int getopt(int argc, char * const argv[], const char *optstring); extern char *optarg; extern int optind, opterr, optopt; #include int getopt_long(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex); int getopt_long_only(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex); DESCRIPTION The getopt() function parses the command line arguments. Its arguments argc and argv are the argument count and array as passed to the main() function on program invoca- tion. An element of argv that starts with `-' (and is not exactly "-" or "--") 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 the external variable optind and a static variable 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 EOF. Then optind is the index in argv of the first argv- element that is not an option. optstring is a string containing the legitimate option characters. If such a character is followed by a colon, the option requires an argument, so getopt places a pointer to the following text in the same argv-element, or the text of the following argv-element, in optarg. Two colons mean an option takes an optional arg; if there is text in the current argv-element, it is returned in optarg, otherwise optarg is set to zero. By default, getargs() permutes the contents of argv as it scans, so that eventually all the non-options are at the GNU Aug 30, 1995 1 GETOPT(3) Linux Programmer's Manual GETOPT(3) end. Two other modes are also implemented. If the first character of optstring is `+' or the environment variable POSIXLY_CORRECT is set, then option processing stops as soon as a non-option argument is encountered. If the first character of optstring is `-', then each non-option argv-element is handled as if it were the argument of an option with character code 1. (This is used by programs that were written to expect options and other argv-ele- ments in any order and that care about the ordering of the two.) The special argument `--' forces an end of option- scanning regardless of the scanning mode. If getopt() does not recognize an option character, it prints an error message to stderr, stores the character in optopt, and returns `?'. The calling program may prevent the error message by setting opterr to 0. The getopt_long() function works like getopt() except that it also accepts long options, started out by two dashes. Long option names may be abbreviated if the abbreviation is unique or is an exact match for some defined option. A long option may take a parameter, of the form --arg=param or --arg param. longopts is a pointer to the first element of an array of struct option declared in as struct option { const char *name; int has_arg; int *flag; int val; }; The meanings of the different fields are: name is the name of the long option. has_arg is: no_argument (or 0) if the option does not take an argument, required_argument (or 1) if the option requires an argument, or optional_argument (or 2) if the option takes an optional argument. flag specifies how results are returned for a long option. If flag is NULL, then getopt_long() returns val. (For example, the calling program may set val to the equivalent short option character.) Otherwise, getopt_long() returns 0, and flag points to a variable which is set to val if the option is found, but left unchanged if the option is not found. val is the value to return, or to load into the GNU Aug 30, 1995 2 GETOPT(3) Linux Programmer's Manual GETOPT(3) variable pointed to by flag. The last element of the array has to be filled with zeroes. If longindex is not NULL, it points to a variable which is set to the index of the long option relative to longopts. getopt_long_only() is like getopt_long(), but `-' as well as `--' can indicate a long option. If an option that starts with `-' (not `--') doesn't match a long option, but does match a short option, it is parsed as a short option instead. RETURN VALUE The getopt() function returns the option character if the option was found successfully, `:' if there was a missing parameter for one of the options, `?' for an unknown option character, or EOF for the end of the option list. getopt_long() and getopt_long_only() also return the option character when a short option is recognized. For a long option, they return val if flag is NULL, and 0 other- wise. Error and EOF returns are the same as for getopt(), plus `?' for an ambiguous match or an extraneous parame- ter. ENVIRONMENT VARIABLES POSIXLY_CORRECT If this is set, then option processing stops as soon as a non-option argument is encountered. EXAMPLE The following example program, from the source code, illustrates the use of getopt_long() with most of its fea- tures. #include int main (argc, argv) int argc; char **argv; { int c; int digit_optind = 0; while (1) { int this_option_optind = optind ? optind : 1; int option_index = 0; static struct option long_options[] = { {"add", 1, 0, 0}, GNU Aug 30, 1995 3 GETOPT(3) Linux Programmer's Manual GETOPT(3) {"append", 0, 0, 0}, {"delete", 1, 0, 0}, {"verbose", 0, 0, 0}, {"create", 1, 0, 'c'}, {"file", 1, 0, 0}, {0, 0, 0, 0} }; c = getopt_long (argc, argv, "abc:d:012", long_options, &option_index); if (c == -1) break; switch (c) { case 0: printf ("option %s", long_options[option_index].name); if (optarg) printf (" with arg %s", optarg); printf ("\n"); break; case '0': case '1': case '2': if (digit_optind != 0 && digit_optind != this_option_optind) printf ("digits occur in two different argv-elements.\n"); digit_optind = this_option_optind; printf ("option %c\n", c); break; case 'a': printf ("option a\n"); break; case 'b': printf ("option b\n"); break; case 'c': printf ("option c with value `%s'\n", optarg); break; case 'd': printf ("option d with value `%s'\n", optarg); break; case '?': break; default: printf ("?? getopt returned character code 0%o ??\n", c); } } GNU Aug 30, 1995 4 GETOPT(3) Linux Programmer's Manual GETOPT(3) if (optind < argc) { printf ("non-option ARGV-elements: "); while (optind < argc) printf ("%s ", argv[optind++]); printf ("\n"); } exit (0); } BUGS This manpage is confusing. CONFORMING TO getopt(): POSIX.1, provided the environment variable POSIXLY_CORRECT is set. Otherwise, the elements of argv aren't really const, because we permute them. We pretend they're const in the prototype to be compatible with other systems. GNU Aug 30, 1995 5 OpenSC-0.26.1/src/common/compat_getopt_main.c000066400000000000000000000243241474147347300210460ustar00rootroot00000000000000/* * copy - test program for my getopt() re-implementation * * This program is in the public domain. */ #define COPYRIGHT \ "This program is in the public domain." /* for isprint(), printf(), fopen(), perror(), getenv(), strcmp(), etc. */ #include #include #include #include /* for my getopt() re-implementation */ #include "compat_getopt.h" #undef VERSION #define VERSION "0.3" /* the default verbosity level is 0 (no verbose reporting) */ static unsigned verbose = 0; /* print version and copyright information */ static void version(char *progname) { printf("%s version %s\n" "%s\n", progname, VERSION, COPYRIGHT); } /* print a help summary */ static void help(char *progname) { printf("Usage: %s [options] [FILE]...\n" "Options:\n" "-h or -help show this message and exit\n" "-append append to the output file\n" "-o FILE or\n" "-output FILE send output to FILE (default is stdout)\n" "-r or --rotate rotate letters 13 positions (rot13)\n" "-rNUM or\n" "--rotate=NUM rotate letters NUM positions\n" "-truncate truncate the output file " "(this is the default)\n" "-v or -verbose increase the level of verbosity by 1" "(the default is 0)\n" "-vNUM or\n" "-verbose=NUM set the level of verbosity to NUM\n" "-V or -version print program version and exit\n" "\n" "This program reads the specified FILEs " "(or stdin if none are given)\n" "and writes their bytes to the specified output FILE " "(or stdout if none is\n" "given.) It can optionally rotate letters.\n", progname); } /* print usage information to stderr */ static void usage(char *progname) { fprintf(stderr, "Summary: %s [-help] [-version] [options] [FILE]...\n", progname); } /* input file handler -- returns nonzero or exit()s on failure */ static int handle(char *progname, FILE *infile, const char *infilename, FILE *outfile, const char *outfilename, int rotate) { int c; unsigned long bytes_copied = 0; if (verbose > 2) { fprintf(stderr, "%s: copying from `%s' to `%s'\n", progname, infilename, outfilename); } while ((c = getc(infile)) != EOF) { if (rotate && isalpha(c)) { const char *letters = "abcdefghijklmnopqrstuvwxyz"; char *match; if ((match = strchr(letters, tolower(c)))) { char rc = letters[(match - letters + rotate) % 26]; if (isupper(c)) rc = toupper((unsigned char)rc); c = rc; } } if (putc(c, outfile) == EOF) { perror(outfilename); exit(1); } bytes_copied ++; } if (! feof(infile)) { perror(infilename); return 1; } if (verbose > 2) { fprintf(stderr, "%s: %lu bytes copied from `%s' to `%s'\n", progname, bytes_copied, infilename, outfilename); } return 0; } /* argument parser and dispatcher */ int main(int argc, char * argv[]) { /* the program name */ char *progname = argv[0]; /* during argument parsing, opt contains the return value from getopt() */ int opt; /* the output filename is initially 0 (a.k.a. stdout) */ const char *outfilename = 0; /* the default return value is initially 0 (success) */ int retval = 0; /* initially we truncate */ int append = 0; /* initially we don't rotate letters */ int rotate = 0; /* short options string */ const char *shortopts = "Vho:r::v::"; /* long options list */ struct option longopts[] = { /* name, has_arg, flag, val */ /* longind */ { "append", no_argument, 0, 0 }, /* 0 */ { "truncate", no_argument, 0, 0 }, /* 1 */ { "version", no_argument, 0, 'V' }, /* 3 */ { "help", no_argument, 0, 'h' }, /* 4 */ { "output", required_argument, 0, 'o' }, /* 5 */ { "rotate", optional_argument, 0, 'r' }, /* 6 */ { "verbose", optional_argument, 0, 'v' }, /* 7 */ /* end-of-list marker */ { 0, 0, 0, 0 } }; /* long option list index */ int longind = 0; /* * print a warning when the POSIXLY_CORRECT environment variable will * interfere with argument placement */ if (getenv("POSIXLY_CORRECT")) { fprintf(stderr, "%s: " "Warning: implicit argument reordering disallowed by " "POSIXLY_CORRECT\n", progname); } /* parse all options from the command line */ while ((opt = getopt_long_only(argc, argv, shortopts, longopts, &longind)) != -1) switch (opt) { case 0: /* a long option without an equivalent short option */ switch (longind) { case 0: /* -append */ append = 1; break; case 1: /* -truncate */ append = 0; break; default: /* something unexpected has happened */ fprintf(stderr, "%s: " "getopt_long_only unexpectedly returned %d for `--%s'\n", progname, opt, longopts[longind].name); return 1; } break; case 'V': /* -version */ version(progname); return 0; case 'h': /* -help */ help(progname); return 0; case 'r': /* -rotate[=NUM] */ if (optarg) { /* we use this while trying to parse a numeric argument */ char ignored; if (sscanf(optarg, "%d%c", &rotate, &ignored) != 1) { fprintf(stderr, "%s: " "rotation `%s' is not a number\n", progname, optarg); usage(progname); return 2; } /* normalize rotation */ while (rotate < 0) { rotate += 26; } rotate %= 26; } else rotate = 13; break; case 'o': /* -output=FILE */ outfilename = optarg; /* we allow "-" as a synonym for stdout here */ if (optarg && !strcmp(optarg, "-")) { outfilename = 0; } break; case 'v': /* -verbose[=NUM] */ if (optarg) { /* we use this while trying to parse a numeric argument */ char ignored; if (sscanf(optarg, "%u%c", &verbose, &ignored) != 1) { fprintf(stderr, "%s: " "verbosity level `%s' is not a number\n", progname, optarg); usage(progname); return 2; } } else verbose ++; break; case '?': /* getopt_long_only noticed an error */ usage(progname); return 2; default: /* something unexpected has happened */ fprintf(stderr, "%s: " "getopt_long_only returned an unexpected value (%d)\n", progname, opt); return 1; } /* re-open stdout to outfilename, if requested */ if (outfilename) { if (! freopen(outfilename, (append ? "a" : "w"), stdout)) { perror(outfilename); return 1; } } else { /* make a human-readable version of the output filename "-" */ outfilename = "stdout"; /* you can't truncate stdout */ append = 1; } if (verbose) { fprintf(stderr, "%s: verbosity level is %u; %s `%s'; rotation %d\n", progname, verbose, (append ? "appending to" : "truncating"), outfilename, rotate); } if (verbose > 1) { fprintf(stderr, "%s: %d input file(s) were given\n", progname, ((argc > optind) ? (argc - optind) : 0)); } if (verbose > 3) { fprintf(stderr, "\topterr: %d\n\toptind: %d\n\toptopt: %d (%c)\n\toptarg: %s\n", opterr, optind, optopt, optopt, optarg ? optarg : "(null)"); } /* handle each of the input files (or stdin, if no files were given) */ if (optind < argc) { int argindex; for (argindex = optind; argindex < argc; argindex ++) { const char *infilename = argv[argindex]; FILE *infile; /* we allow "-" as a synonym for stdin here */ if (! strcmp(infilename, "-")) { infile = stdin; infilename = "stdin"; } else if (! (infile = fopen(infilename, "r"))) { perror(infilename); retval = 1; continue; } if (handle(progname, infile, argv[optind], stdout, outfilename, rotate)) { retval = 1; fclose(infile); continue; } if ((infile != stdin) && fclose(infile)) { perror(infilename); retval = 1; } } } else { retval = handle(progname, stdin, "stdin", stdout, outfilename, rotate); } /* close stdout */ if (fclose(stdout)) { perror(outfilename); return 1; } if (verbose > 3) { fprintf(stderr, "%s: normal return, exit code is %d\n", progname, retval); } return retval; } OpenSC-0.26.1/src/common/compat_getpass.c000066400000000000000000000017241474147347300202050ustar00rootroot00000000000000#ifdef HAVE_CONFIG_H #include "config.h" #endif #ifndef HAVE_GETPASS /* empty file if getpass is available */ #include #include "compat_getpass.h" #ifdef _WIN32 #include #else #include #include #include int _getch(void) { struct termios old, mute; int c; tcgetattr(STDIN_FILENO, &old); mute = old; mute.c_lflag &= ~(ICANON|ECHO); if (0 != tcsetattr(STDIN_FILENO, TCSANOW, &mute)) { /* XXX an error happened */ /* We prefer to print the password, i.e. ignore the error, * rather than to deny the service, i.e. return something like '\0' */ } c = getchar(); tcsetattr(STDIN_FILENO, TCSANOW, &old); return c; } #endif char *getpass(const char *prompt) { static char buf[128]; size_t i; fputs(prompt, stderr); fflush(stderr); for (i = 0; i < sizeof(buf) - 1; i++) { buf[i] = _getch(); if (buf[i] == '\r') break; } buf[i] = 0; fputs("\n", stderr); return buf; } #endif /* HAVE_GETPASS */ OpenSC-0.26.1/src/common/compat_getpass.h000066400000000000000000000001751474147347300202110ustar00rootroot00000000000000#ifndef __COMPAT_GETPASS_H #define __COMPAT_GETPASS_H #ifndef HAVE_GETPASS char *getpass (const char *prompt); #endif #endif OpenSC-0.26.1/src/common/compat_overflow.c000066400000000000000000000031141474147347300203750ustar00rootroot00000000000000/* * 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 OpenSC-0.26.1/src/common/compat_overflow.h000066400000000000000000000051071474147347300204060ustar00rootroot00000000000000/* * 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 OpenSC-0.26.1/src/common/compat_report_rangecheckfailure.c000066400000000000000000000023301474147347300235660ustar00rootroot00000000000000/* * Copyright (C) 2015 Vincent Le Toux * * 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_RANGECHECKFAILURE /* empty file if __report_rangecheckfailure is available */ // do not fail when linked with /GS dll and when /GS is not available #ifdef _WIN32 #if defined(_MSC_VER) && (_MSC_VER < 1700) // only for vs 2012 or later #include __declspec(noreturn) void __cdecl __report_rangecheckfailure() { ExitProcess(1); } #endif #endif #endif OpenSC-0.26.1/src/common/compat_strlcat.c000066400000000000000000000047151474147347300202160ustar00rootroot00000000000000/* $OpenBSD: strlcat.c,v 1.2 1999/06/17 16:28:58 millert Exp $ */ /*- * Copyright (c) 1998 Todd C. Miller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED ``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 * THE AUTHOR 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #if !defined(HAVE_DECL_STRLCAT) || !HAVE_DECL_STRLCAT #include #include #include "compat_strlcat.h" /* * Appends src to string dst of size siz (unlike strncat, siz is the * full size of dst, not space left). At most siz-1 characters * will be copied. Always NUL terminates (unless siz <= strlen(dst)). * Returns strlen(src) + MIN(siz, strlen(initial dst)). * If retval >= siz, truncation occurred. */ size_t strlcat(char *dst, const char *src, size_t siz) { char *d = dst; const char *s = src; size_t n = siz; size_t dlen; /* Find the end of dst and adjust bytes left but don't go past end */ while (n-- != 0 && *d != '\0') d++; dlen = d - dst; n = siz - dlen; if (n == 0) return(dlen + strlen(s)); while (*s != '\0') { if (n != 1) { *d++ = *s; n--; } s++; } *d = '\0'; return(dlen + (s - src)); /* count does not include NUL */ } #endif OpenSC-0.26.1/src/common/compat_strlcat.h000066400000000000000000000005351474147347300202170ustar00rootroot00000000000000/** * @file * @brief prototypes of strlcat() imported from OpenBSD */ #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 OpenSC-0.26.1/src/common/compat_strlcpy.3000066400000000000000000000104531474147347300201560ustar00rootroot00000000000000.\" $OpenBSD: strlcpy.3,v 1.18 2005/08/06 03:24:19 jaredy Exp $ .\" .\" Copyright (c) 1998, 2000 Todd C. Miller .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .Dd June 22, 1998 .Dt STRLCPY 3 .Os .Sh NAME .Nm strlcpy , .Nm strlcat .Nd size-bounded string copying and concatenation .Sh SYNOPSIS .Fd #include .Ft size_t .Fn strlcpy "char *dst" "const char *src" "size_t size" .Ft size_t .Fn strlcat "char *dst" "const char *src" "size_t size" .Sh DESCRIPTION The .Fn strlcpy and .Fn strlcat functions copy and concatenate strings respectively. They are designed to be safer, more consistent, and less error prone replacements for .Xr strncpy 3 and .Xr strncat 3 . Unlike those functions, .Fn strlcpy and .Fn strlcat take the full size of the buffer (not just the length) and guarantee to NUL-terminate the result (as long as .Fa size is larger than 0 or, in the case of .Fn strlcat , as long as there is at least one byte free in .Fa dst ) . Note that a byte for the NUL should be included in .Fa size . Also note that .Fn strlcpy and .Fn strlcat only operate on true .Dq C strings. This means that for .Fn strlcpy .Fa src must be NUL-terminated and for .Fn strlcat both .Fa src and .Fa dst must be NUL-terminated. .Pp The .Fn strlcpy function copies up to .Fa size - 1 characters from the NUL-terminated string .Fa src to .Fa dst , NUL-terminating the result. .Pp The .Fn strlcat function appends the NUL-terminated string .Fa src to the end of .Fa dst . It will append at most .Fa size - strlen(dst) - 1 bytes, NUL-terminating the result. .Sh RETURN VALUES The .Fn strlcpy and .Fn strlcat functions return the total length of the string they tried to create. For .Fn strlcpy that means the length of .Fa src . For .Fn strlcat that means the initial length of .Fa dst plus the length of .Fa src . While this may seem somewhat confusing, it was done to make truncation detection simple. .Pp Note, however, that if .Fn strlcat traverses .Fa size characters without finding a NUL, the length of the string is considered to be .Fa size and the destination string will not be NUL-terminated (since there was no space for the NUL). This keeps .Fn strlcat from running off the end of a string. In practice this should not happen (as it means that either .Fa size is incorrect or that .Fa dst is not a proper .Dq C string). The check exists to prevent potential security problems in incorrect code. .Sh EXAMPLES The following code fragment illustrates the simple case: .Bd -literal -offset indent char *s, *p, buf[BUFSIZ]; \&... (void)strlcpy(buf, s, sizeof(buf)); (void)strlcat(buf, p, sizeof(buf)); .Ed .Pp To detect truncation, perhaps while building a pathname, something like the following might be used: .Bd -literal -offset indent char *dir, *file, pname[MAXPATHLEN]; \&... if (strlcpy(pname, dir, sizeof(pname)) >= sizeof(pname)) goto toolong; if (strlcat(pname, file, sizeof(pname)) >= sizeof(pname)) goto toolong; .Ed .Pp Since it is known how many characters were copied the first time, things can be sped up a bit by using a copy instead of an append: .Bd -literal -offset indent char *dir, *file, pname[MAXPATHLEN]; size_t n; \&... n = strlcpy(pname, dir, sizeof(pname)); if (n >= sizeof(pname)) goto toolong; if (strlcpy(pname + n, file, sizeof(pname) - n) >= sizeof(pname) - n) goto toolong; .Ed .Pp However, one may question the validity of such optimizations, as they defeat the whole purpose of .Fn strlcpy and .Fn strlcat . As a matter of fact, the first version of this manual page got it wrong. .Sh SEE ALSO .Xr snprintf 3 , .Xr strncat 3 , .Xr strncpy 3 .Sh HISTORY The .Fn strlcpy and .Fn strlcat functions first appeared in .Ox 2.4 . OpenSC-0.26.1/src/common/compat_strlcpy.c000066400000000000000000000032721474147347300202370ustar00rootroot00000000000000/* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */ /* * Copyright (c) 1998 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #if !defined(HAVE_DECL_STRLCPY) || !HAVE_DECL_STRLCPY #include #include #include "compat_strlcpy.h" /* * Copy src to string dst of size siz. At most siz-1 characters * will be copied. Always NUL terminates (unless siz == 0). * Returns strlen(src); if retval >= siz, truncation occurred. */ size_t _strlcpy(char *dst, const char *src, size_t siz) { char *d = dst; const char *s = src; size_t n = siz; /* Copy as many bytes as will fit */ if (n != 0) { while (--n != 0) { if ((*d++ = *s++) == '\0') break; } } /* Not enough room in dst, add NUL and traverse rest of src */ if (n == 0) { if (siz != 0) *d = '\0'; /* NUL-terminate dst */ while (*s++) ; } return(s - src - 1); /* count does not include NUL */ } #endif /* HAVE_STRLCPY */ OpenSC-0.26.1/src/common/compat_strlcpy.h000066400000000000000000000035341474147347300202450ustar00rootroot00000000000000/* * MUSCLE SmartCard Development ( http://pcsclite.alioth.debian.org/pcsclite.html ) * * Copyright (C) 2004-2010 * Ludovic Rousseau * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. 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. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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 * @brief prototypes of strlcpy() imported from OpenBSD */ #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 OpenSC-0.26.1/src/common/compat_strnlen.c000066400000000000000000000022011474147347300202130ustar00rootroot00000000000000/* $OpenBSD: strnlen.c,v 1.5 2014/06/10 04:17:37 deraadt Exp $ */ /* * Copyright (c) 2010 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifndef HAVE_STRNLEN #include #include size_t strnlen(const char *str, size_t maxlen) { const char *cp; for (cp = str; maxlen != 0 && *cp != '\0'; cp++, maxlen--) ; return (size_t)(cp - str); } #endif OpenSC-0.26.1/src/common/compat_strnlen.h000066400000000000000000000004201474147347300202210ustar00rootroot00000000000000/** * @file * @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 OpenSC-0.26.1/src/common/constant-time.h000066400000000000000000000056721474147347300177740ustar00rootroot00000000000000/* 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 */ OpenSC-0.26.1/src/common/libpkcs11.c000066400000000000000000000065271474147347300167730ustar00rootroot00000000000000/* * Convenience pkcs11 library that can be linked into an application, * and will bind to a specific pkcs11 module. * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "pkcs11/pkcs11.h" #include "common/libscdl.h" #include "common/libpkcs11.h" #define MAGIC 0xd00bed00 struct sc_pkcs11_module { unsigned int _magic; void *handle; }; typedef struct sc_pkcs11_module sc_pkcs11_module_t; /* * Load a module - this will load the shared object, call * C_Initialize, and get the list of function pointers */ void * 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; } mod->_magic = MAGIC; if (mspec == NULL) { free(mod); return NULL; } mod->handle = sc_dlopen(mspec); if (mod->handle == NULL) { fprintf(stderr, "sc_dlopen failed: %s\n", sc_dlerror()); 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"); if (!c_get_function_list) goto failed; rv = c_get_function_list(funcs); if (rv == CKR_OK) return (void *) mod; else { fprintf(stderr, "C_GetFunctionList failed %lx", rv); rv = C_UnloadModule((void *) mod); if (rv == CKR_OK) mod = NULL; /* already freed */ } failed: free(mod); return NULL; } /* * Unload a pkcs11 module. * The calling application is responsible for cleaning up * and calling C_Finalize */ CK_RV C_UnloadModule(void *module) { sc_pkcs11_module_t *mod = (sc_pkcs11_module_t *) module; if (!mod || mod->_magic != MAGIC) return CKR_ARGUMENTS_BAD; if (mod->handle != NULL && sc_dlclose(mod->handle) < 0) return CKR_FUNCTION_FAILED; free(mod); return CKR_OK; } OpenSC-0.26.1/src/common/libpkcs11.h000066400000000000000000000020241474147347300167640ustar00rootroot00000000000000/* * libpkcs11.h: Function definitions for the PKCS#11 module loading minilibrary * * Copyright (C) 2010 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 */ #ifndef __LIBPKCS11_H #define __LIBPKCS11_H void *C_LoadModule(const char *name, CK_FUNCTION_LIST_PTR_PTR); CK_RV C_UnloadModule(void *module); #endif OpenSC-0.26.1/src/common/libscdl.c000066400000000000000000000032731474147347300166110ustar00rootroot00000000000000/* * libscdl.c: wrappers for dlfcn() interfaces * * Copyright (C) 2010 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 #endif #include "libscdl.h" #ifdef _WIN32 #include #include void *sc_dlopen(const char *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) { return GetProcAddress((HMODULE)handle, symbol); } const char *sc_dlerror() { return "LoadLibrary/GetProcAddress failed"; } int sc_dlclose(void *handle) { return FreeLibrary((HMODULE)handle); } #else #include void *sc_dlopen(const char *filename) { return dlopen(filename, RTLD_LAZY); } void *sc_dlsym(void *handle, const char *symbol) { return dlsym(handle, symbol); } const char *sc_dlerror(void) { return dlerror(); } int sc_dlclose(void *handle) { return dlclose(handle); } #endif OpenSC-0.26.1/src/common/libscdl.h000066400000000000000000000020711474147347300166110ustar00rootroot00000000000000/* * libscdl.h: Function definitions for the dynamic loading minilibrary. * * Copyright (C) 2010 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 */ #ifndef __LIBSCDL_H #define __LIBSCDL_H void *sc_dlopen(const char *filename); void *sc_dlsym(void *handle, const char *symbol); int sc_dlclose(void *handle); const char *sc_dlerror(void); #endif OpenSC-0.26.1/src/common/simclist.c000066400000000000000000001405771474147347300170350ustar00rootroot00000000000000/* * Copyright (c) 2007,2008,2009,2010 Mij * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * SimCList library. See http://mij.oltrelinux.com/devel/simclist */ /* SimCList implementation, version 1.5, with local modifications */ #include #include #include /* for setting errno */ #include #if !defined(_WIN32) #include /* for htons() */ #include #ifdef HAVE_SYS_TIME_H #include /* for gettimeofday() */ #endif #include #else #include #endif #ifdef SIMCLIST_DUMPRESTORE #ifndef _WIN32 #include /* for READ_ERRCHECK() and write() */ #endif #include /* for open() etc */ #endif #include /* for time() for random seed */ #include /* for open()'s access modes S_IRUSR etc */ #include #ifdef SIMCLIST_DUMPRESTORE /* convert 64bit integers from host to network format */ #define hton64(x) (\ htons(1) == 1 ? \ (uint64_t)x /* big endian */ \ : /* little endian */ \ ((uint64_t)((((uint64_t)(x) & 0xff00000000000000ULL) >> 56) | \ (((uint64_t)(x) & 0x00ff000000000000ULL) >> 40) | \ (((uint64_t)(x) & 0x0000ff0000000000ULL) >> 24) | \ (((uint64_t)(x) & 0x000000ff00000000ULL) >> 8) | \ (((uint64_t)(x) & 0x00000000ff000000ULL) << 8) | \ (((uint64_t)(x) & 0x0000000000ff0000ULL) << 24) | \ (((uint64_t)(x) & 0x000000000000ff00ULL) << 40) | \ (((uint64_t)(x) & 0x00000000000000ffULL) << 56))) \ ) /* convert 64bit integers from network to host format */ #define ntoh64(x) (hton64(x)) #endif /* some OSes don't have EPROTO (eg OpenBSD) */ #ifndef EPROTO #define EPROTO EIO #endif /* disable asserts */ #ifndef SIMCLIST_DEBUG #ifndef NDEBUG #define NDEBUG #endif #endif #include #ifdef SIMCLIST_WITH_THREADS /* limit (approx) to the number of threads running * for threaded operations. Only meant when * SIMCLIST_WITH_THREADS is defined */ #define SIMCLIST_MAXTHREADS 2 #endif /* * how many elems to keep as spare. During a deletion, an element * can be saved in a "free-list", not free()d immediately. When * latter insertions are performed, spare elems can be used instead * of malloc()ing new elems. * * about this param, some values for appending * 10 million elems into an empty list: * (#, time[sec], gain[%], gain/no[%]) * 0 2,164 0,00 0,00 <-- feature disabled * 1 1,815 34,9 34,9 * 2 1,446 71,8 35,9 <-- MAX gain/no * 3 1,347 81,7 27,23 * 5 1,213 95,1 19,02 * 8 1,064 110,0 13,75 * 10 1,015 114,9 11,49 <-- MAX gain w/ likely sol * 15 1,019 114,5 7,63 * 25 0,985 117,9 4,72 * 50 1,088 107,6 2,15 * 75 1,016 114,8 1,53 * 100 0,988 117,6 1,18 * 150 1,022 114,2 0,76 * 200 0,939 122,5 0,61 <-- MIN time */ #ifndef SIMCLIST_MAX_SPARE_ELEMS #define SIMCLIST_MAX_SPARE_ELEMS 5 #endif #ifdef SIMCLIST_WITH_THREADS #include #endif #include "simclist.h" /* minimum number of elements for sorting with quicksort instead of insertion */ #define SIMCLIST_MINQUICKSORTELS 24 /* list dump declarations */ #define SIMCLIST_DUMPFORMAT_VERSION 1 /* (short integer) version of fileformat managed by _dump* and _restore* functions */ #define SIMCLIST_DUMPFORMAT_HEADERLEN 30 /* length of the header */ /* header for a list dump */ struct list_dump_header_s { uint16_t ver; /* version */ int64_t timestamp; /* dump timestamp */ int32_t rndterm; /* random value terminator -- terminates the data sequence */ uint32_t totlistlen; /* sum of every element' size, bytes */ uint32_t numels; /* number of elements */ uint32_t elemlen; /* bytes length of an element, for constant-size lists, <= 0 otherwise */ int32_t listhash; /* hash of the list at the time of dumping, or 0 if to be ignored */ }; /* deletes tmp from list, with care wrt its position (head, tail, middle) */ static int list_drop_elem(list_t *simclist_restrict l, struct list_entry_s *tmp, unsigned int pos); /* set default values for initialized lists */ static int list_attributes_setdefaults(list_t *simclist_restrict l); #ifndef NDEBUG /* check whether the list internal REPresentation is valid -- Costs O(n) */ static int list_repOk(const list_t *simclist_restrict l); /* check whether the list attribute set is valid -- Costs O(1) */ static int list_attrOk(const list_t *simclist_restrict l); #endif /* do not inline, this is recursive */ static void list_sort_quicksort(list_t *simclist_restrict l, int versus, unsigned int first, struct list_entry_s *fel, unsigned int last, struct list_entry_s *lel); static simclist_inline void list_sort_selectionsort(list_t *simclist_restrict l, int versus, unsigned int first, struct list_entry_s *fel, unsigned int last, struct list_entry_s *lel); static void *list_get_minmax(const list_t *simclist_restrict l, int versus); static simclist_inline struct list_entry_s *list_findpos(const list_t *simclist_restrict l, int posstart); #ifdef SIMCLIST_DUMPRESTORE /* write() decorated with error checking logic */ #define WRITE_ERRCHECK(fd, msgbuf, msglen) do { \ if (write(fd, msgbuf, msglen) < 0) return -1; \ } while (0); /* READ_ERRCHECK() decorated with error checking logic */ #define READ_ERRCHECK(fd, msgbuf, msglen) do { \ if (read(fd, msgbuf, msglen) != msglen) { \ /*errno = EPROTO;*/ \ free(buf); \ return -1; \ } \ } while (0); #endif /* * Random Number Generator * * The user is expected to seed the RNG (ie call srand()) if * SIMCLIST_SYSTEM_RNG is defined. * * Otherwise, a self-contained RNG based on LCG is used; see * http://en.wikipedia.org/wiki/Linear_congruential_generator . * * Facts pro local RNG: * 1. no need for the user to call srand() on his own * 2. very fast, possibly faster than OS * 3. avoid interference with user's RNG * * Facts pro system RNG: * 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. */ #ifdef SIMCLIST_SYSTEM_RNG /* keep track whether we initialized already (non-0) or not (0) */ static unsigned random_seed = 0; /* use local RNG */ static simclist_inline void seed_random() { if (random_seed == 0) random_seed = (unsigned)getpid() ^ (unsigned)time(NULL); } static simclist_inline long get_random() { random_seed = (1664525 * random_seed + 1013904223); return random_seed; } #else /* use OS's random generator */ # define seed_random() # define get_random() (rand()) #endif /* list initialization */ int list_init(list_t *simclist_restrict l) { if (l == NULL) { return -1; } memset(l, 0, sizeof *l); seed_random(); l->numels = 0; /* head/tail sentinels and mid pointer */ l->head_sentinel = (struct list_entry_s *)malloc(sizeof(struct list_entry_s)); l->tail_sentinel = (struct list_entry_s *)malloc(sizeof(struct list_entry_s)); if (l->tail_sentinel == NULL || l->head_sentinel == NULL) { return -1; } l->head_sentinel->next = l->tail_sentinel; l->tail_sentinel->prev = l->head_sentinel; l->head_sentinel->prev = l->tail_sentinel->next = l->mid = NULL; l->head_sentinel->data = l->tail_sentinel->data = NULL; /* iteration attributes */ l->iter_active = 0; l->iter_pos = 0; l->iter_curentry = NULL; /* free-list attributes */ l->spareelsnum = 0; l->spareels = (struct list_entry_s **)malloc(SIMCLIST_MAX_SPARE_ELEMS * sizeof(struct list_entry_s *)); if (l->spareels == NULL) { return -1; } #ifdef SIMCLIST_WITH_THREADS l->threadcount = 0; #endif if (0 != list_attributes_setdefaults(l)) { return -1; } assert(list_repOk(l)); assert(list_attrOk(l)); return 0; } void list_destroy(list_t *simclist_restrict l) { unsigned int i; list_clear(l); for (i = 0; i < l->spareelsnum; i++) { free(l->spareels[i]); } free(l->spareels); free(l->head_sentinel); free(l->tail_sentinel); } int list_attributes_setdefaults(list_t *simclist_restrict l) { l->attrs.comparator = NULL; l->attrs.seeker = NULL; /* also free() element data when removing and element from the list */ l->attrs.meter = NULL; l->attrs.copy_data = 0; l->attrs.hasher = NULL; /* serializer/unserializer */ l->attrs.serializer = NULL; l->attrs.unserializer = NULL; assert(list_attrOk(l)); return 0; } /* setting list properties */ int list_attributes_comparator(list_t *simclist_restrict l, element_comparator comparator_fun) { if (l == NULL) return -1; l->attrs.comparator = comparator_fun; assert(list_attrOk(l)); return 0; } int list_attributes_seeker(list_t *simclist_restrict l, element_seeker seeker_fun) { if (l == NULL) return -1; l->attrs.seeker = seeker_fun; assert(list_attrOk(l)); return 0; } int list_attributes_copy(list_t *simclist_restrict l, element_meter metric_fun, int copy_data) { if (l == NULL || (metric_fun == NULL && copy_data != 0)) return -1; l->attrs.meter = metric_fun; l->attrs.copy_data = copy_data; assert(list_attrOk(l)); return 0; } int list_attributes_hash_computer(list_t *simclist_restrict l, element_hash_computer hash_computer_fun) { if (l == NULL) return -1; l->attrs.hasher = hash_computer_fun; assert(list_attrOk(l)); return 0; } int list_attributes_serializer(list_t *simclist_restrict l, element_serializer serializer_fun) { if (l == NULL) return -1; l->attrs.serializer = serializer_fun; assert(list_attrOk(l)); return 0; } int list_attributes_unserializer(list_t *simclist_restrict l, element_unserializer unserializer_fun) { if (l == NULL) return -1; l->attrs.unserializer = unserializer_fun; assert(list_attrOk(l)); return 0; } int list_append(list_t *simclist_restrict l, const void *data) { return list_insert_at(l, data, l->numels); } int list_prepend(list_t *simclist_restrict l, const void *data) { return list_insert_at(l, data, 0); } void *list_fetch(list_t *simclist_restrict l) { return list_extract_at(l, 0); } void *list_get_at(const list_t *simclist_restrict l, unsigned int pos) { struct list_entry_s *tmp; tmp = list_findpos(l, pos); return (tmp != NULL ? tmp->data : NULL); } void *list_get_max(const list_t *simclist_restrict l) { return list_get_minmax(l, +1); } void *list_get_min(const list_t *simclist_restrict l) { return list_get_minmax(l, -1); } /* REQUIRES {list->numels >= 1} * return the min (versus < 0) or max value (v > 0) in l */ static void *list_get_minmax(const list_t *simclist_restrict l, int versus) { void *curminmax; struct list_entry_s *s; if (l->attrs.comparator == NULL || l->numels == 0) return NULL; curminmax = l->head_sentinel->next->data; for (s = l->head_sentinel->next->next; s != l->tail_sentinel; s = s->next) { if (l->attrs.comparator(curminmax, s->data) * versus > 0) curminmax = s->data; } return curminmax; } /* set tmp to point to element at index posstart in l */ static simclist_inline struct list_entry_s *list_findpos(const list_t *simclist_restrict l, int posstart) { struct list_entry_s *ptr; float x; int i; if (l->head_sentinel == NULL || l->tail_sentinel == NULL) return NULL; /* accept 1 slot overflow for fetching head and tail sentinels */ if (posstart < -1 || posstart > (int)l->numels) return NULL; 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++); } else if (x < 0.5) { /* second quarter: get to posstart from mid */ for (i = (l->numels-1)/2, ptr = l->mid; i > posstart; ptr = ptr->prev, i--); } else if (x <= 0.75) { /* third quarter: get to posstart from mid */ for (i = (l->numels-1)/2, ptr = l->mid; i < posstart; ptr = ptr->next, i++); } else { /* fourth quarter: get to posstart from tail */ for (i = l->numels, ptr = l->tail_sentinel; i > posstart; ptr = ptr->prev, i--); } return ptr; } void *list_extract_at(list_t *simclist_restrict l, unsigned int pos) { struct list_entry_s *tmp; void *data; if (l->iter_active || pos >= l->numels) return NULL; tmp = list_findpos(l, pos); if (tmp == NULL) { return NULL; } data = tmp->data; tmp->data = NULL; /* save data from list_drop_elem() free() */ list_drop_elem(l, tmp, pos); l->numels--; assert(list_repOk(l)); return data; } int list_insert_at(list_t *simclist_restrict l, const void *data, unsigned int pos) { struct list_entry_s *lent, *succ, *prec; if (l->iter_active || pos > l->numels) return -1; /* this code optimizes malloc() with a free-list */ if (l->spareelsnum > 0) { lent = l->spareels[l->spareelsnum-1]; l->spareelsnum--; } else { lent = (struct list_entry_s *)malloc(sizeof(struct list_entry_s)); if (lent == NULL) { return -1; } } if (l->attrs.copy_data) { /* make room for user' data (has to be copied) */ 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); } else { lent->data = (void*)data; } /* 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; prec->next = lent; lent->prev = prec; lent->next = succ; succ->prev = lent; l->numels++; /* fix mid pointer */ if (l->numels == 1) { /* first element, set pointer */ l->mid = lent; } else if (l->numels % 2) { /* now odd */ if (pos >= (l->numels-1)/2) l->mid = l->mid->next; } else { /* now even */ if (pos <= (l->numels-1)/2) l->mid = l->mid->prev; } assert(list_repOk(l)); return 1; } int list_delete(list_t *simclist_restrict l, const void *data) { int pos, r; pos = list_locate(l, data); if (pos < 0) return -1; r = list_delete_at(l, pos); if (r < 0) return -1; assert(list_repOk(l)); return 0; } int list_delete_at(list_t *simclist_restrict l, unsigned int pos) { struct list_entry_s *delendo; if (l->iter_active || pos >= l->numels) return -1; delendo = list_findpos(l, pos); list_drop_elem(l, delendo, pos); l->numels--; assert(list_repOk(l)); return 0; } int list_delete_range(list_t *simclist_restrict l, unsigned int posstart, unsigned int posend) { struct list_entry_s *lastvalid, *tmp, *tmp2; unsigned int i; int movedx; unsigned int numdel, midposafter; if (l->iter_active || posend < posstart || posend >= l->numels) return -1; tmp = list_findpos(l, posstart); /* first el to be deleted */ if (tmp == NULL) { return -1; } lastvalid = tmp->prev; /* last valid element */ numdel = posend - posstart + 1; midposafter = (l->numels-1-numdel)/2; midposafter = midposafter < posstart ? midposafter : midposafter+numdel; movedx = midposafter - (l->numels-1)/2; if (movedx > 0) { /* move right */ for (i = 0; i < (unsigned int)movedx; l->mid = l->mid->next, i++); } else { /* move left */ movedx = -movedx; for (i = 0; i < (unsigned int)movedx; l->mid = l->mid->prev, i++); } assert(posstart == 0 || lastvalid != l->head_sentinel); i = posstart; if (l->attrs.copy_data) { /* also free element data */ for (; i <= posend; i++) { tmp2 = tmp; tmp = tmp->next; if (tmp2->data != NULL) free(tmp2->data); if (l->spareelsnum < SIMCLIST_MAX_SPARE_ELEMS) { l->spareels[l->spareelsnum++] = tmp2; } else { free(tmp2); } } } else { /* only free containers */ for (; i <= posend; i++) { tmp2 = tmp; tmp = tmp->next; if (l->spareelsnum < SIMCLIST_MAX_SPARE_ELEMS) { l->spareels[l->spareelsnum++] = tmp2; } else { free(tmp2); } } } assert(i == posend+1 && (posend != l->numels || tmp == l->tail_sentinel)); lastvalid->next = tmp; tmp->prev = lastvalid; l->numels -= posend - posstart + 1; assert(list_repOk(l)); return 0; } int list_clear(list_t *simclist_restrict l) { struct list_entry_s *s; if (l->iter_active) return -1; if (l->head_sentinel && l->tail_sentinel) { if (l->attrs.copy_data) { /* also free user data */ /* spare a loop conditional with two loops: spareing elems and freeing elems */ for (s = l->head_sentinel->next; l->spareelsnum < SIMCLIST_MAX_SPARE_ELEMS && s != l->tail_sentinel; s = s->next) { /* move elements as spares as long as there is room */ if (s->data != NULL) free(s->data); l->spareels[l->spareelsnum++] = s; } while (s != l->tail_sentinel) { /* free the remaining elems */ if (s->data != NULL) free(s->data); s = s->next; free(s->prev); } l->head_sentinel->next = l->tail_sentinel; l->tail_sentinel->prev = l->head_sentinel; } else { /* only free element containers */ /* spare a loop conditional with two loops: spareing elems and freeing elems */ for (s = l->head_sentinel->next; l->spareelsnum < SIMCLIST_MAX_SPARE_ELEMS && s != l->tail_sentinel; s = s->next) { /* move elements as spares as long as there is room */ l->spareels[l->spareelsnum++] = s; } while (s != l->tail_sentinel) { /* free the remaining elems */ s = s->next; free(s->prev); } l->head_sentinel->next = l->tail_sentinel; l->tail_sentinel->prev = l->head_sentinel; } } l->numels = 0; l->mid = NULL; assert(list_repOk(l)); return 0; } unsigned int list_size(const list_t *simclist_restrict l) { return l->numels; } int list_empty(const list_t *simclist_restrict l) { return (l->numels == 0); } int list_locate(const list_t *simclist_restrict l, const void *data) { struct list_entry_s *el; int pos = 0; if (l->head_sentinel == NULL || l->tail_sentinel == NULL) return -1; if (l->attrs.comparator != NULL) { /* use comparator */ for (el = l->head_sentinel->next; el != l->tail_sentinel; el = el->next, pos++) { if (l->attrs.comparator(data, el->data) == 0) break; } } else { /* compare references */ for (el = l->head_sentinel->next; el != l->tail_sentinel; el = el->next, pos++) { if (el->data == data) break; } } if (el == l->tail_sentinel) return -1; return pos; } void *list_seek(list_t *simclist_restrict l, const void *indicator) { const struct list_entry_s *iter; if (l->attrs.seeker == NULL) return NULL; if (l->head_sentinel == NULL || l->tail_sentinel == NULL) return NULL; for (iter = l->head_sentinel->next; iter != l->tail_sentinel; iter = iter->next) { if (l->attrs.seeker(iter->data, indicator) != 0) return iter->data; } return NULL; } int list_contains(const list_t *simclist_restrict l, const void *data) { return (list_locate(l, data) >= 0); } int list_concat(const list_t *l1, const list_t *l2, list_t *simclist_restrict dest) { struct list_entry_s *el, *srcel; unsigned int cnt; int err; if (l1 == NULL || l2 == NULL || dest == NULL || l1 == dest || l2 == dest) return -1; if (l1->head_sentinel == NULL || l1->tail_sentinel == NULL || l2->head_sentinel == NULL || l2->tail_sentinel == NULL) return -1; if (0 != list_init(dest)) { return -1; } dest->numels = l1->numels + l2->numels; if (dest->numels == 0) return 0; /* copy list1 */ srcel = l1->head_sentinel->next; el = dest->head_sentinel; while (srcel != l1->tail_sentinel) { el->next = (struct list_entry_s *)malloc(sizeof(struct list_entry_s)); if (el->next == NULL) { return -1; } el->next->prev = el; el = el->next; el->data = srcel->data; srcel = srcel->next; } dest->mid = el; /* approximate position (adjust later) */ /* copy list 2 */ srcel = l2->head_sentinel->next; while (srcel != l2->tail_sentinel) { el->next = (struct list_entry_s *)malloc(sizeof(struct list_entry_s)); if (el->next == NULL) { return -1; } el->next->prev = el; el = el->next; el->data = srcel->data; srcel = srcel->next; } el->next = dest->tail_sentinel; dest->tail_sentinel->prev = el; /* fix mid pointer */ err = l2->numels - l1->numels; if ((err+1)/2 > 0) { /* correct pos RIGHT (err-1)/2 moves */ err = (err+1)/2; 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; dest->mid && cnt < (unsigned int)err; cnt++) dest->mid = dest->mid->prev; } assert(!(list_repOk(l1) && list_repOk(l2)) || list_repOk(dest)); return 0; } int list_sort(list_t *simclist_restrict l, int versus) { if (l->iter_active || l->attrs.comparator == NULL) /* cannot modify list in the middle of an iteration */ return -1; if (l->numels <= 1) return 0; if (l->head_sentinel == NULL || l->tail_sentinel == NULL) return -1; list_sort_quicksort(l, versus, 0, l->head_sentinel->next, l->numels-1, l->tail_sentinel->prev); assert(list_repOk(l)); return 0; } #ifdef SIMCLIST_WITH_THREADS struct list_sort_wrappedparams { list_t *simclist_restrict l; int versus; unsigned int first, last; struct list_entry_s *fel, *lel; }; static void *list_sort_quicksort_threadwrapper(void *wrapped_params) { struct list_sort_wrappedparams *wp = (struct list_sort_wrappedparams *)wrapped_params; list_sort_quicksort(wp->l, wp->versus, wp->first, wp->fel, wp->last, wp->lel); free(wp); pthread_exit(NULL); return NULL; } #endif static simclist_inline void list_sort_selectionsort(list_t *simclist_restrict l, int versus, unsigned int first, struct list_entry_s *fel, unsigned int last, struct list_entry_s *lel) { struct list_entry_s *cursor, *toswap, *firstunsorted; void *tmpdata; if (last <= first) /* <= 1-element lists are always sorted */ return; for (firstunsorted = fel; firstunsorted != lel; firstunsorted = firstunsorted->next) { /* find min or max in the remainder of the list */ for (toswap = firstunsorted, cursor = firstunsorted->next; cursor != lel->next; cursor = cursor->next) if (l->attrs.comparator(toswap->data, cursor->data) * -versus > 0) toswap = cursor; if (toswap != firstunsorted) { /* swap firstunsorted with toswap */ tmpdata = firstunsorted->data; firstunsorted->data = toswap->data; toswap->data = tmpdata; } } } static void list_sort_quicksort(list_t *simclist_restrict l, int versus, unsigned int first, struct list_entry_s *fel, unsigned int last, struct list_entry_s *lel) { unsigned int pivotid; unsigned int i; register struct list_entry_s *pivot; struct list_entry_s *left, *right; void *tmpdata; #ifdef SIMCLIST_WITH_THREADS pthread_t tid; int traised; #endif if (last <= first) /* <= 1-element lists are always sorted */ return; if (last - first+1 <= SIMCLIST_MINQUICKSORTELS) { list_sort_selectionsort(l, versus, first, fel, last, lel); return; } /* base of iteration: one element list */ if (! (last > first)) return; pivotid = (get_random() % (last - first + 1)); /* pivotid = (last - first + 1) / 2; */ /* find pivot */ if (pivotid < (last - first + 1)/2) { for (i = 0, pivot = fel; i < pivotid; pivot = pivot->next, i++); } else { for (i = last - first, pivot = lel; i > pivotid; pivot = pivot->prev, i--); } /* smaller PIVOT bigger */ left = fel; right = lel; /* iterate --- left ---> PIV <--- right --- */ while (left != pivot && right != pivot) { for (; left != pivot && (l->attrs.comparator(left->data, pivot->data) * -versus <= 0); left = left->next); /* left points to a smaller element, or to pivot */ for (; right != pivot && (l->attrs.comparator(right->data, pivot->data) * -versus >= 0); right = right->prev); /* right points to a bigger element, or to pivot */ if (left != pivot && right != pivot) { /* swap, then move iterators */ tmpdata = left->data; left->data = right->data; right->data = tmpdata; left = left->next; right = right->prev; } } /* now either left points to pivot (end run), or right */ if (right == pivot) { /* left part longer */ while (left != pivot) { if (l->attrs.comparator(left->data, pivot->data) * -versus > 0) { tmpdata = left->data; left->data = pivot->prev->data; pivot->prev->data = pivot->data; pivot->data = tmpdata; pivot = pivot->prev; pivotid--; if (pivot == left) break; } else { left = left->next; } } } else { /* right part longer */ while (right != pivot) { if (l->attrs.comparator(right->data, pivot->data) * -versus < 0) { /* move current right before pivot */ tmpdata = right->data; right->data = pivot->next->data; pivot->next->data = pivot->data; pivot->data = tmpdata; pivot = pivot->next; pivotid++; if (pivot == right) break; } else { right = right->prev; } } } /* sort sublists A and B : |---A---| pivot |---B---| */ #ifdef SIMCLIST_WITH_THREADS traised = 0; if (pivotid > 0) { /* prepare wrapped args, then start thread */ if (l->threadcount < SIMCLIST_MAXTHREADS-1) { struct list_sort_wrappedparams *wp = (struct list_sort_wrappedparams *)malloc(sizeof(struct list_sort_wrappedparams)); if (wp == NULL) { return -1; } l->threadcount++; traised = 1; wp->l = l; wp->versus = versus; wp->first = first; wp->fel = fel; wp->last = first+pivotid-1; wp->lel = pivot->prev; if (pthread_create(&tid, NULL, list_sort_quicksort_threadwrapper, wp) != 0) { free(wp); traised = 0; list_sort_quicksort(l, versus, first, fel, first+pivotid-1, pivot->prev); } } else { list_sort_quicksort(l, versus, first, fel, first+pivotid-1, pivot->prev); } } if (first + pivotid < last) list_sort_quicksort(l, versus, first+pivotid+1, pivot->next, last, lel); if (traised) { pthread_join(tid, (void **)NULL); l->threadcount--; } #else if (pivotid > 0) list_sort_quicksort(l, versus, first, fel, first+pivotid-1, pivot->prev); if (first + pivotid < last) list_sort_quicksort(l, versus, first+pivotid+1, pivot->next, last, lel); #endif } int list_iterator_start(list_t *simclist_restrict l) { if (l->iter_active) return 0; if (l->head_sentinel == NULL) return -1; l->iter_pos = 0; l->iter_active = 1; l->iter_curentry = l->head_sentinel->next; return 1; } void *list_iterator_next(list_t *simclist_restrict l) { void *toret; if (! l->iter_active) return NULL; toret = l->iter_curentry->data; l->iter_curentry = l->iter_curentry->next; l->iter_pos++; return toret; } int list_iterator_hasnext(const list_t *simclist_restrict l) { if (! l->iter_active) return 0; return (l->iter_pos < l->numels); } int list_iterator_stop(list_t *simclist_restrict l) { if (! l->iter_active) return 0; l->iter_pos = 0; l->iter_active = 0; return 1; } int list_hash(const list_t *simclist_restrict l, list_hash_t *simclist_restrict hash) { struct list_entry_s *x; list_hash_t tmphash; assert(hash != NULL); tmphash = l->numels * 2 + 100; if (l->attrs.hasher == NULL) { #ifdef SIMCLIST_ALLOW_LOCATIONBASED_HASHES /* ENABLE WITH CARE !! */ #warning "Memlocation-based hash is consistent only for testing modification in the same program run." int i; /* only use element references */ for (x = l->head_sentinel->next; x != l->tail_sentinel; x = x->next) { for (i = 0; i < sizeof(x->data); i++) { tmphash += (tmphash ^ (uintptr_t)x->data); } tmphash += tmphash % l->numels; } #else return -1; #endif } else { /* hash each element with the user-given function */ for (x = l->head_sentinel->next; x != l->tail_sentinel; x = x->next) { tmphash += tmphash ^ l->attrs.hasher(x->data); tmphash +=* hash % l->numels; } } *hash = tmphash; return 0; } #ifdef SIMCLIST_DUMPRESTORE /* Workaround for a missing gettimeofday on Windows */ #if defined(_MSC_VER) || defined(__MINGW32__) int gettimeofday(struct timeval* tp, void* tzp) { DWORD t; t = timeGetTime(); tp->tv_sec = t / 1000; tp->tv_usec = t % 1000; return 0; } #endif int list_dump_getinfo_filedescriptor(int fd, list_dump_info_t *simclist_restrict info) { int32_t terminator_head, terminator_tail; uint32_t elemlen; off_t hop; /* version */ READ_ERRCHECK(fd, & info->version, sizeof(info->version)); info->version = ntohs(info->version); if (info->version > SIMCLIST_DUMPFORMAT_VERSION) { errno = EILSEQ; return -1; } /* timestamp */ READ_ERRCHECK(fd, & info->timestamp, sizeof(info->timestamp)); info->timestamp = hton64(info->timestamp); /* list terminator (to check thereafter) */ READ_ERRCHECK(fd, & terminator_head, sizeof(terminator_head)); terminator_head = ntohl(terminator_head); /* list size */ READ_ERRCHECK(fd, & info->list_size, sizeof(info->list_size)); info->list_size = ntohl(info->list_size); /* number of elements */ READ_ERRCHECK(fd, & info->list_numels, sizeof(info->list_numels)); info->list_numels = ntohl(info->list_numels); /* length of each element (for checking for consistency) */ READ_ERRCHECK(fd, & elemlen, sizeof(elemlen)); elemlen = ntohl(elemlen); /* list hash */ READ_ERRCHECK(fd, & info->list_hash, sizeof(info->list_hash)); info->list_hash = ntohl(info->list_hash); /* check consistency */ if (elemlen > 0) { /* constant length, hop by size only */ hop = info->list_size; } else { /* non-constant length, hop by size + all element length blocks */ hop = info->list_size + elemlen*info->list_numels; } if (lseek(fd, hop, SEEK_CUR) == -1) { return -1; } /* read the trailing value and compare with terminator_head */ READ_ERRCHECK(fd, & terminator_tail, sizeof(terminator_tail)); terminator_tail = ntohl(terminator_tail); if (terminator_head == terminator_tail) info->consistent = 1; else info->consistent = 0; return 0; } int list_dump_getinfo_file(const char *simclist_restrict filename, list_dump_info_t *simclist_restrict info) { int fd, ret; fd = open(filename, O_RDONLY, 0); if (fd < 0) return -1; ret = list_dump_getinfo_filedescriptor(fd, info); close(fd); return ret; } int list_dump_filedescriptor(const list_t *simclist_restrict l, int fd, size_t *simclist_restrict len) { struct list_entry_s *x; void *ser_buf; uint32_t bufsize; struct timeval timeofday; struct list_dump_header_s header; if (l->attrs.meter == NULL && l->attrs.serializer == NULL) { errno = ENOTTY; return -1; } /**** DUMP FORMAT **** [ ver timestamp | totlen numels elemlen hash | DATA ] where DATA can be: @ for constant-size list (element size is constant; elemlen > 0) [ elem elem ... elem ] @ for other lists (element size dictated by element_meter each time; elemlen <= 0) [ size elem size elem ... size elem ] all integers are encoded in NETWORK BYTE FORMAT *****/ /* prepare HEADER */ /* version */ header.ver = htons( SIMCLIST_DUMPFORMAT_VERSION ); /* timestamp */ gettimeofday(&timeofday, NULL); header.timestamp = (int64_t)timeofday.tv_sec * 1000000 + (int64_t)timeofday.tv_usec; header.timestamp = hton64(header.timestamp); header.rndterm = htonl((int32_t)get_random()); /* total list size is postprocessed afterwards */ /* number of elements */ header.numels = htonl(l->numels); /* include an hash, if possible */ if (l->attrs.hasher != NULL) { if (htonl(list_hash(l, & header.listhash)) != 0) { /* could not compute list hash! */ return -1; } } else { header.listhash = htonl(0); } header.totlistlen = header.elemlen = 0; /* leave room for the header at the beginning of the file */ if (lseek(fd, SIMCLIST_DUMPFORMAT_HEADERLEN, SEEK_SET) < 0) { /* errno set by lseek() */ return -1; } /* write CONTENT */ if (l->numels > 0) { /* SPECULATE that the list has constant element size */ if (l->attrs.serializer != NULL) { /* user user-specified serializer */ /* get preliminary length of serialized element in header.elemlen */ ser_buf = l->attrs.serializer(l->head_sentinel->next->data, & header.elemlen); free(ser_buf); /* request custom serialization of each element */ for (x = l->head_sentinel->next; x != l->tail_sentinel; x = x->next) { ser_buf = l->attrs.serializer(x->data, &bufsize); header.totlistlen += bufsize; if (header.elemlen != 0) { /* continue on speculation */ if (header.elemlen != bufsize) { free(ser_buf); /* constant element length speculation broken! */ header.elemlen = 0; header.totlistlen = 0; x = l->head_sentinel; if (lseek(fd, SIMCLIST_DUMPFORMAT_HEADERLEN, SEEK_SET) < 0) { /* errno set by lseek() */ return -1; } /* restart from the beginning */ continue; } /* speculation confirmed */ WRITE_ERRCHECK(fd, ser_buf, bufsize); } else { /* speculation found broken */ WRITE_ERRCHECK(fd, & bufsize, sizeof(size_t)); WRITE_ERRCHECK(fd, ser_buf, bufsize); } free(ser_buf); } } else if (l->attrs.meter != NULL) { header.elemlen = (uint32_t)l->attrs.meter(l->head_sentinel->next->data); /* serialize the element straight from its data */ for (x = l->head_sentinel->next; x != l->tail_sentinel; x = x->next) { bufsize = l->attrs.meter(x->data); header.totlistlen += bufsize; if (header.elemlen != 0) { if (header.elemlen != bufsize) { /* constant element length speculation broken! */ header.elemlen = 0; header.totlistlen = 0; x = l->head_sentinel; /* restart from the beginning */ continue; } WRITE_ERRCHECK(fd, x->data, bufsize); } else { WRITE_ERRCHECK(fd, &bufsize, sizeof(size_t)); WRITE_ERRCHECK(fd, x->data, bufsize); } } } /* adjust endianness */ header.elemlen = htonl(header.elemlen); header.totlistlen = htonl(header.totlistlen); } /* write random terminator */ WRITE_ERRCHECK(fd, & header.rndterm, sizeof(header.rndterm)); /* list terminator */ /* write header */ lseek(fd, 0, SEEK_SET); WRITE_ERRCHECK(fd, & header.ver, sizeof(header.ver)); /* version */ WRITE_ERRCHECK(fd, & header.timestamp, sizeof(header.timestamp)); /* timestamp */ WRITE_ERRCHECK(fd, & header.rndterm, sizeof(header.rndterm)); /* random terminator */ WRITE_ERRCHECK(fd, & header.totlistlen, sizeof(header.totlistlen)); /* total length of elements */ WRITE_ERRCHECK(fd, & header.numels, sizeof(header.numels)); /* number of elements */ WRITE_ERRCHECK(fd, & header.elemlen, sizeof(header.elemlen)); /* size of each element, or 0 for independent */ WRITE_ERRCHECK(fd, & header.listhash, sizeof(header.listhash)); /* list hash, or 0 for "ignore" */ /* possibly store total written length in "len" */ if (len != NULL) { *len = sizeof(header) + ntohl(header.totlistlen); } return 0; } int list_restore_filedescriptor(list_t *simclist_restrict l, int fd, size_t *simclist_restrict len) { struct list_dump_header_s header; unsigned long cnt; void *buf = NULL; uint32_t elsize, totreadlen, totmemorylen; memset(& header, 0, sizeof(header)); /* read header */ /* version */ READ_ERRCHECK(fd, &header.ver, sizeof(header.ver)); header.ver = ntohs(header.ver); if (header.ver != SIMCLIST_DUMPFORMAT_VERSION) { errno = EILSEQ; return -1; } /* timestamp */ READ_ERRCHECK(fd, & header.timestamp, sizeof(header.timestamp)); /* list terminator */ READ_ERRCHECK(fd, & header.rndterm, sizeof(header.rndterm)); header.rndterm = ntohl(header.rndterm); /* total list size */ READ_ERRCHECK(fd, & header.totlistlen, sizeof(header.totlistlen)); header.totlistlen = ntohl(header.totlistlen); /* number of elements */ READ_ERRCHECK(fd, & header.numels, sizeof(header.numels)); header.numels = ntohl(header.numels); /* length of every element, or '0' = variable */ READ_ERRCHECK(fd, & header.elemlen, sizeof(header.elemlen)); header.elemlen = ntohl(header.elemlen); /* list hash, or 0 = 'ignore' */ READ_ERRCHECK(fd, & header.listhash, sizeof(header.listhash)); header.listhash = ntohl(header.listhash); /* read content */ totreadlen = totmemorylen = 0; if (header.elemlen > 0) { /* elements have constant size = header.elemlen */ if (l->attrs.unserializer != NULL) { /* use unserializer */ buf = malloc(header.elemlen); if (buf == NULL) { return -1; } for (cnt = 0; cnt < header.numels; cnt++) { READ_ERRCHECK(fd, buf, header.elemlen); list_append(l, l->attrs.unserializer(buf, & elsize)); totmemorylen += elsize; } } else { /* copy verbatim into memory */ for (cnt = 0; cnt < header.numels; cnt++) { buf = malloc(header.elemlen); if (buf == NULL) { return -1; } READ_ERRCHECK(fd, buf, header.elemlen); list_append(l, buf); } totmemorylen = header.numels * header.elemlen; } totreadlen = header.numels * header.elemlen; } else { /* elements have variable size. Each element is preceded by its size */ if (l->attrs.unserializer != NULL) { /* use unserializer */ for (cnt = 0; cnt < header.numels; cnt++) { READ_ERRCHECK(fd, & elsize, sizeof(elsize)); buf = malloc((size_t)elsize); if (buf == NULL) { return -1; } READ_ERRCHECK(fd, buf, elsize); totreadlen += elsize; list_append(l, l->attrs.unserializer(buf, & elsize)); totmemorylen += elsize; } } else { /* copy verbatim into memory */ for (cnt = 0; cnt < header.numels; cnt++) { READ_ERRCHECK(fd, & elsize, sizeof(elsize)); buf = malloc(elsize); if (buf == NULL) { return -1; } READ_ERRCHECK(fd, buf, elsize); totreadlen += elsize; list_append(l, buf); } totmemorylen = totreadlen; } } READ_ERRCHECK(fd, &elsize, sizeof(elsize)); /* read list terminator */ elsize = ntohl(elsize); /* possibly verify the list consistency */ /* wrt hash */ /* don't do that if (header.listhash != 0 && header.listhash != list_hash(l)) { errno = ECANCELED; return -1; } */ /* wrt header */ if (totreadlen != header.totlistlen && (int32_t)elsize == header.rndterm) { errno = EPROTO; return -1; } /* wrt file */ if (lseek(fd, 0, SEEK_CUR) != lseek(fd, 0, SEEK_END)) { errno = EPROTO; return -1; } if (len != NULL) { *len = totmemorylen; } return 0; } int list_dump_file(const list_t *simclist_restrict l, const char *simclist_restrict filename, size_t *simclist_restrict len) { int fd, mode; size_t sizetoret; mode = O_RDWR | O_CREAT | O_TRUNC; #ifndef _WIN32 mode |= S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; #endif fd = open(filename, mode); if (fd < 0) return -1; sizetoret = list_dump_filedescriptor(l, fd, len); close(fd); return sizetoret; } int list_restore_file(list_t *simclist_restrict l, const char *simclist_restrict filename, size_t *simclist_restrict len) { int fd; size_t totdata; fd = open(filename, O_RDONLY, 0); if (fd < 0) return -1; totdata = list_restore_filedescriptor(l, fd, len); close(fd); return totdata; } #endif /* ifdef SIMCLIST_DUMPRESTORE */ static int list_drop_elem(list_t *simclist_restrict l, struct list_entry_s *tmp, unsigned int pos) { if (tmp == NULL) return -1; /* fix mid pointer. This is wrt the PRE situation */ if (l->numels % 2) { /* now odd */ /* sort out the base case by hand */ if (l->numels == 1) l->mid = NULL; else if (pos >= l->numels/2) l->mid = l->mid->prev; } else { /* now even */ if (pos < l->numels/2) l->mid = l->mid->next; } tmp->prev->next = tmp->next; tmp->next->prev = tmp->prev; /* free what's to be freed */ if (l->attrs.copy_data && tmp->data != NULL) free(tmp->data); if (l->spareels != NULL && l->spareelsnum < SIMCLIST_MAX_SPARE_ELEMS) { l->spareels[l->spareelsnum++] = tmp; } else { free(tmp); } return 0; } /* ready-made comparators and meters */ #define SIMCLIST_NUMBER_COMPARATOR(type) int list_comparator_##type(const void *a, const void *b) { return( *(type *)a < *(type *)b) - (*(type *)a > *(type *)b); } SIMCLIST_NUMBER_COMPARATOR(int8_t) SIMCLIST_NUMBER_COMPARATOR(int16_t) SIMCLIST_NUMBER_COMPARATOR(int32_t) SIMCLIST_NUMBER_COMPARATOR(int64_t) SIMCLIST_NUMBER_COMPARATOR(uint8_t) SIMCLIST_NUMBER_COMPARATOR(uint16_t) SIMCLIST_NUMBER_COMPARATOR(uint32_t) SIMCLIST_NUMBER_COMPARATOR(uint64_t) SIMCLIST_NUMBER_COMPARATOR(float) SIMCLIST_NUMBER_COMPARATOR(double) int list_comparator_string(const void *a, const void *b) { return strcmp((const char *)b, (const char *)a); } /* ready-made metric functions */ #define SIMCLIST_METER(type) size_t list_meter_##type(const void *el) { if (el) { /* kill compiler whinge */ } return sizeof(type); } SIMCLIST_METER(int8_t) SIMCLIST_METER(int16_t) SIMCLIST_METER(int32_t) SIMCLIST_METER(int64_t) SIMCLIST_METER(uint8_t) SIMCLIST_METER(uint16_t) SIMCLIST_METER(uint32_t) SIMCLIST_METER(uint64_t) SIMCLIST_METER(float) SIMCLIST_METER(double) size_t list_meter_string(const void *el) { return strlen((const char *)el) + 1; } /* ready-made hashing functions */ #define SIMCLIST_HASHCOMPUTER(type) list_hash_t list_hashcomputer_##type(const void *el) { return (list_hash_t)(*(type *)el); } SIMCLIST_HASHCOMPUTER(int8_t) SIMCLIST_HASHCOMPUTER(int16_t) SIMCLIST_HASHCOMPUTER(int32_t) SIMCLIST_HASHCOMPUTER(int64_t) SIMCLIST_HASHCOMPUTER(uint8_t) SIMCLIST_HASHCOMPUTER(uint16_t) SIMCLIST_HASHCOMPUTER(uint32_t) SIMCLIST_HASHCOMPUTER(uint64_t) SIMCLIST_HASHCOMPUTER(float) SIMCLIST_HASHCOMPUTER(double) list_hash_t list_hashcomputer_string(const void *el) { size_t l; list_hash_t hash = 123; const char *str = (const char *)el; char plus; for (l = 0; str[l] != '\0'; l++) { if (l) plus = hash ^ str[l]; else plus = hash ^ (str[l] - str[0]); hash += (plus << (CHAR_BIT * (l % sizeof(list_hash_t)))); } return hash; } #ifndef NDEBUG static int list_repOk(const list_t *simclist_restrict l) { int ok, i; struct list_entry_s *s; ok = (l != NULL) && ( /* head/tail checks */ (l->head_sentinel != NULL && l->tail_sentinel != NULL) && (l->head_sentinel != l->tail_sentinel) && (l->head_sentinel->prev == NULL && l->tail_sentinel->next == NULL) && /* empty list */ (l->numels > 0 || (l->mid == NULL && l->head_sentinel->next == l->tail_sentinel && l->tail_sentinel->prev == l->head_sentinel)) && /* spare elements checks */ l->spareelsnum <= SIMCLIST_MAX_SPARE_ELEMS ); if (!ok) return 0; if (l->numels >= 1) { /* correct referencing */ for (i = -1, s = l->head_sentinel; i < (int)(l->numels-1)/2 && s->next != NULL; i++, s = s->next) { if (s->next->prev != s) break; } ok = (i == (int)(l->numels-1)/2 && l->mid == s); if (!ok) return 0; for (; s->next != NULL; i++, s = s->next) { if (s->next->prev != s) break; } ok = (i == (int)l->numels && s == l->tail_sentinel); } return ok; } static int list_attrOk(const list_t *simclist_restrict l) { int ok; ok = (l->attrs.copy_data == 0 || l->attrs.meter != NULL); return ok; } #endif OpenSC-0.26.1/src/common/simclist.h000066400000000000000000000776211474147347300170410ustar00rootroot00000000000000/* * Copyright (c) 2007,2008 Mij * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * SimCList library. See http://mij.oltrelinux.com/devel/simclist */ #ifndef SIMCLIST_H #define SIMCLIST_H #ifdef __cplusplus extern "C" { #endif /* work around lack of inttypes.h support in broken Microsoft Visual Studio compilers */ #if defined(_MSC_VER) #include typedef UINT8 uint8_t; typedef UINT16 uint16_t; typedef ULONG32 uint32_t; typedef UINT64 uint64_t; typedef INT8 int8_t; typedef INT16 int16_t; typedef LONG32 int32_t; typedef INT64 int64_t; #else #include /* (u)int*_t */ #endif #include #include /* bases on OpenSSL's version in e_os2.h */ #if !defined(inline) /* Be friend of both C90 and C99 compilers */ # if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* "inline" and "restrict" are keywords */ # define simclist_inline inline # elif defined(__GNUC__) && __GNUC__>=2 # define simclist_inline __inline__ # elif defined(_MSC_VER) # define simclist_inline __inline # else # define simclist_inline # endif #else /* use what caller wants as inline may be from config.h */ # define simclist_inline inline /* inline */ #endif /* bases on OpenSSL's version in e_os2.h */ /* On MacOS C++ is used for tokend */ #if !defined(restrict) /* Be friend of both C90 and C99 compilers */ # if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* "inline" and "restrict" are keywords */ # define simclist_restrict restrict /* restrict */ # elif defined(__GNUC__) && __GNUC__>=2 # define simclist_restrict __restrict__ # elif defined(_MSC_VER) # define simclist_restrict __restrict # else # define simclist_restrict # endif #else /* use what caller wants as restrict may be from config.h */ # define simclist_restrict restrict #endif /** * Type representing list hashes. * * This is a signed integer value. */ typedef int32_t list_hash_t; #ifdef SIMCLIST_DUMPRESTORE typedef struct { uint16_t version; /* dump version */ int64_t timestamp; /* when the list has been dumped, microseconds from UNIX epoch */ uint32_t list_size; uint32_t list_numels; list_hash_t list_hash; /* hash of the list when dumped, or 0 if invalid */ uint32_t dumpsize; int consistent; /* 1 if the dump is verified complete/consistent; 0 otherwise */ } list_dump_info_t; #endif /** * a comparator of elements. * * A comparator of elements is a function that: * -# receives two references to elements a and b * -# returns {<0, 0, >0} if (a > b), (a == b), (a < b) respectively * * It is responsibility of the function to handle possible NULL values. */ typedef int (*element_comparator)(const void *a, const void *b); /** * a seeker of elements. * * An element seeker is a function that: * -# receives a reference to an element el * -# receives a reference to some indicator data * -# returns non-0 if the element matches the indicator, 0 otherwise * * It is responsibility of the function to handle possible NULL values in any * argument. */ typedef int (*element_seeker)(const void *el, const void *indicator); /** * an element length meter. * * An element meter is a function that: * -# receives the reference to an element el * -# returns its size in bytes * * It is responsibility of the function to handle possible NULL values. */ typedef size_t (*element_meter)(const void *el); /** * a function computing the hash of elements. * * An hash computing function is a function that: * -# receives the reference to an element el * -# returns a hash value for el * * It is responsibility of the function to handle possible NULL values. */ typedef list_hash_t (*element_hash_computer)(const void *el); /** * a function for serializing an element. * * 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 responsibility of the function to handle possible NULL values, * returning a NULL buffer and a 0 buffer length. * * These functions have 3 goals: * -# "freeze" and "flatten" the memory representation of the element * -# provide a portable (wrt byte order, or type size) representation of the element, if the dump can be used on different sw/hw combinations * -# possibly extract a compressed representation of the element * * @param el reference to the element data * @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 serialize_buffer); /** * a function for un-serializing an element. * * An unserializer function accomplishes the inverse operation of the * serializer function. An unserializer function is one that gets a * serialized representation of an element and turns it backe to the original * element. The serialized representation is passed as a reference to a buffer * with its data, and the function allocates and returns the buffer containing * the original element, and it sets the length of this buffer into the * integer passed by reference. * * @param data reference to the buffer with the serialized representation of the element * @param data_len reference to the location where to store the length of the data in the buffer returned * @return reference to a buffer with the original, unserialized representation of the element */ typedef void *(*element_unserializer)(const void *simclist_restrict data, uint32_t *simclist_restrict data_len); /* [private-use] list entry -- olds actual user datum */ struct list_entry_s { void *data; /* doubly-linked list service references */ struct list_entry_s *next; struct list_entry_s *prev; }; /* [private-use] list attributes */ struct list_attributes_s { /* user-set routine for comparing list elements */ element_comparator comparator; /* user-set routing for seeking elements */ element_seeker seeker; /* user-set routine for determining the length of an element */ element_meter meter; int copy_data; /* user-set routine for computing the hash of an element */ element_hash_computer hasher; /* user-set routine for serializing an element */ element_serializer serializer; /* user-set routine for unserializing an element */ element_unserializer unserializer; }; /** list object */ typedef struct { struct list_entry_s *head_sentinel; struct list_entry_s *tail_sentinel; struct list_entry_s *mid; unsigned int numels; /* array of spare elements */ struct list_entry_s **spareels; unsigned int spareelsnum; #ifdef SIMCLIST_WITH_THREADS /* how many threads are currently running */ unsigned int threadcount; #endif /* service variables for list iteration */ int iter_active; unsigned int iter_pos; struct list_entry_s *iter_curentry; /* list attributes */ struct list_attributes_s attrs; } list_t; /** * initialize a list object for use. * * @param l must point to a user-provided memory location * @return 0 for success. -1 for failure */ int list_init(list_t *simclist_restrict l); /** * completely remove the list from memory. * * This function is the inverse of list_init(). It is meant to be called when * the list is no longer going to be used. Elements and possible memory taken * for internal use are freed. * * @param l list to destroy */ void list_destroy(list_t *simclist_restrict l); /** * set the comparator function for list elements. * * Comparator functions are used for searching and sorting. If NULL is passed * as reference to the function, the comparator is disabled. * * @param l list to operate * @param comparator_fun pointer to the actual comparator function * @return 0 if the attribute was successfully set; -1 otherwise * * @see element_comparator() */ int list_attributes_comparator(list_t *simclist_restrict l, element_comparator comparator_fun); /** * set a seeker function for list elements. * * Seeker functions are used for finding elements. If NULL is passed as reference * to the function, the seeker is disabled. * * @param l list to operate * @param seeker_fun pointer to the actual seeker function * @return 0 if the attribute was successfully set; -1 otherwise * * @see element_seeker() */ int list_attributes_seeker(list_t *simclist_restrict l, element_seeker seeker_fun); /** * require to free element data when list entry is removed (default: don't free). * * [ advanced preference ] * * By default, when an element is removed from the list, it disappears from * the list by its actual data is not free()d. With this option, every * deletion causes element data to be freed. * * It is responsibility of this function to correctly handle NULL values, if * NULL elements are inserted into the list. * * @param l list to operate * @param metric_fun pointer to the actual metric function * @param copy_data 0: do not free element data (default); non-0: do free * @return 0 if the attribute was successfully set; -1 otherwise * * @see element_meter() * @see list_meter_int8_t() * @see list_meter_int16_t() * @see list_meter_int32_t() * @see list_meter_int64_t() * @see list_meter_uint8_t() * @see list_meter_uint16_t() * @see list_meter_uint32_t() * @see list_meter_uint64_t() * @see list_meter_float() * @see list_meter_double() * @see list_meter_string() */ int list_attributes_copy(list_t *simclist_restrict l, element_meter metric_fun, int copy_data); /** * set the element hash computing function for the list elements. * * [ advanced preference ] * * An hash can be requested depicting the list status at a given time. An hash * only depends on the elements and their order. By default, the hash of an * element is only computed on its reference. With this function, the user can * set a custom function computing the hash of an element. If such function is * provided, the list_hash() function automatically computes the list hash using * the custom function instead of simply referring to element references. * * @param l list to operate * @param hash_computer_fun pointer to the actual hash computing function * @return 0 if the attribute was successfully set; -1 otherwise * * @see element_hash_computer() */ int list_attributes_hash_computer(list_t *simclist_restrict l, element_hash_computer hash_computer_fun); /** * set the element serializer function for the list elements. * * [ advanced preference ] * * Serialize functions are used for dumping the list to some persistent * storage. The serializer function is called for each element; it is passed * a reference to the element and a reference to a size_t object. It will * provide (and return) the buffer with the serialization of the element and * fill the size_t object with the length of this serialization data. * * @param l list to operate * @param serializer_fun pointer to the actual serializer function * @return 0 if the attribute was successfully set; -1 otherwise * * @see element_serializer() * @see list_dump_filedescriptor() * @see list_restore_filedescriptor() */ int list_attributes_serializer(list_t *simclist_restrict l, element_serializer serializer_fun); /** * set the element unserializer function for the list elements. * * [ advanced preference ] * * Unserialize functions are used for restoring the list from some persistent * storage. The unserializer function is called for each element segment read * from the storage; it is passed the segment and a reference to an integer. * It shall allocate and return a buffer compiled with the resumed memory * representation of the element, and set the integer value to the length of * this buffer. * * @param l list to operate * @param unserializer_fun pointer to the actual unserializer function * @return 0 if the attribute was successfully set; -1 otherwise * * @see element_unserializer() * @see list_dump_filedescriptor() * @see list_restore_filedescriptor() */ int list_attributes_unserializer(list_t *simclist_restrict l, element_unserializer unserializer_fun); /** * append data at the end of the list. * * This function is useful for adding elements with a FIFO/queue policy. * * @param l list to operate * @param data pointer to user data to append * * @return 1 for success. < 0 for failure */ int list_append(list_t *simclist_restrict l, const void *data); /** * insert data in the head of the list. * * This function is useful for adding elements with a LIFO/Stack policy. * * @param l list to operate * @param data pointer to user data to append * * @return 1 for success. < 0 for failure */ int list_prepend(list_t *simclist_restrict l, const void *simclist_restrict data); /** * extract the element in the top of the list. * * This function is for using a list with a FIFO/queue policy. * * @param l list to operate * @return reference to user datum, or NULL on errors */ void *list_fetch(list_t *simclist_restrict l); /** * retrieve an element at a given position. * * @param l list to operate * @param pos [0,size-1] position index of the element wanted * @return reference to user datum, or NULL on errors */ void *list_get_at(const list_t *simclist_restrict l, unsigned int pos); /** * return the maximum element of the list. * * @warning Requires a comparator function to be set for the list. * * Returns the maximum element with respect to the comparator function output. * * @see list_attributes_comparator() * * @param l list to operate * @return the reference to the element, or NULL */ void *list_get_max(const list_t *simclist_restrict l); /** * return the minimum element of the list. * * @warning Requires a comparator function to be set for the list. * * Returns the minimum element with respect to the comparator function output. * * @see list_attributes_comparator() * * @param l list to operate * @return the reference to the element, or NULL */ void *list_get_min(const list_t *simclist_restrict l); /** * retrieve and remove from list an element at a given position. * * @param l list to operate * @param pos [0,size-1] position index of the element wanted * @return reference to user datum, or NULL on errors */ void *list_extract_at(list_t *simclist_restrict l, unsigned int pos); /** * insert an element at a given position. * * @param l list to operate * @param data reference to data to be inserted * @param pos [0,size-1] position index to insert the element at * @return positive value on success. Negative on failure */ int list_insert_at(list_t *simclist_restrict l, const void *data, unsigned int pos); /** * 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 occurrence is removed. * If a comparator function was not set, elements are compared by reference. * Otherwise, the comparator is used to match the element. * * @param l list to operate * @param data reference of the element to search for * @return 0 on success. Negative value on failure * * @see list_attributes_comparator() * @see list_delete_at() */ int list_delete(list_t *simclist_restrict l, const void *data); /** * expunge an element at a given position from the list. * * @param l list to operate * @param pos [0,size-1] position index of the element to be deleted * @return 0 on success. Negative value on failure */ int list_delete_at(list_t *simclist_restrict l, unsigned int pos); /** * expunge an array of elements from the list, given their position range. * * @param l list to operate * @param posstart [0,size-1] position index of the first element to be deleted * @param posend [posstart,size-1] position of the last element to be deleted * @return the number of elements successfully removed */ int list_delete_range(list_t *simclist_restrict l, unsigned int posstart, unsigned int posend); /** * clear all the elements off of the list. * * The element data will not be freed. * * @see list_delete_range() * @see list_size() * * @param l list to operate * @return the number of elements in the list before cleaning */ int list_clear(list_t *simclist_restrict l); /** * inspect the number of elements in the list. * * @param l list to operate * @return number of elements currently held by the list */ unsigned int list_size(const list_t *simclist_restrict l); /** * inspect whether the list is empty. * * @param l list to operate * @return 0 iff the list is not empty * * @see list_size() */ int list_empty(const list_t *simclist_restrict l); /** * find the position of an element in a list. * * @warning Requires a comparator function to be set for the list. * * Inspects the given list looking for the given element; if the element * is found, its position into the list is returned. * Elements are inspected comparing references if a comparator has not been * set. Otherwise, the comparator is used to find the element. * * @param l list to operate * @param data reference of the element to search for * @return position of element in the list, or <0 if not found * * @see list_attributes_comparator() * @see list_get_at() */ int list_locate(const list_t *simclist_restrict l, const void *data); /** * returns an element given an indicator. * * @warning Requires a seeker function to be set for the list. * * Inspect the given list looking with the seeker if an element matches * an indicator. If such element is found, the reference to the element * is returned. * * @param l list to operate * @param indicator indicator data to pass to the seeker along with elements * @return reference to the element accepted by the seeker, or NULL if none found */ void *list_seek(list_t *simclist_restrict l, const void *indicator); /** * inspect whether some data is member of the list. * * @warning Requires a comparator function to be set for the list. * * By default, a per-reference comparison is accomplished. That is, * the data is in list if any element of the list points to the same * location of data. * A "semantic" comparison is accomplished, otherwise, if a comparator * function has been set previously, with list_attributes_comparator(); * in which case, the given data reference is believed to be in list iff * comparator_fun(elementdata, userdata) == 0 for any element in the list. * * @param l list to operate * @param data reference to the data to search * @return 0 iff the list does not contain data as an element * * @see list_attributes_comparator() */ int list_contains(const list_t *simclist_restrict l, const void *data); /** * concatenate two lists * * Concatenates one list with another, and stores the result into a * 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 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. * * @param l1 base list * @param l2 list to append to the base * @param dest reference to the destination list * @return 0 for success, -1 for errors */ int list_concat(const list_t *l1, const list_t *l2, list_t *simclist_restrict dest); /** * sort list elements. * * @warning Requires a comparator function to be set for the list. * * Sorts the list in ascending or descending order as specified by the versus * flag. The algorithm chooses autonomously what algorithm is best suited for * sorting the list wrt its current status. * * @param l list to operate * @param versus positive: order small to big; negative: order big to small * @return 0: sorting went OK non-0: errors happened * * @see list_attributes_comparator() */ int list_sort(list_t *simclist_restrict l, int versus); /** * start an iteration session. * * This function prepares the list to be iterated. * * @param l list to operate * @return 0 if the list cannot be currently iterated. >0 otherwise * * @see list_iterator_stop() */ int list_iterator_start(list_t *simclist_restrict l); /** * return the next element in the iteration session. * * @param l list to operate * @return element datum, or NULL on errors */ void *list_iterator_next(list_t *simclist_restrict l); /** * inspect whether more elements are available in the iteration session. * * @param l list to operate * @return 0 iff no more elements are available. */ int list_iterator_hasnext(const list_t *simclist_restrict l); /** * end an iteration session. * * @param l list to operate * @return 0 iff the iteration session cannot be stopped */ int list_iterator_stop(list_t *simclist_restrict l); /** * return the hash of the current status of the list. * * @param l list to operate * @param hash where the resulting hash is put * * @return 0 for success; <0 for failure */ int list_hash(const list_t *simclist_restrict l, list_hash_t *simclist_restrict hash); #ifdef SIMCLIST_DUMPRESTORE /** * get meta information on a list dump on filedescriptor. * * [ advanced function ] * * Extracts the meta information from a SimCList dump located in a file * descriptor. The file descriptor must be open and positioned at the * beginning of the SimCList dump block. * * @param fd file descriptor to get metadata from * @param info reference to a dump metainformation structure to fill * @return 0 for success; <0 for failure * * @see list_dump_filedescriptor() */ int list_dump_getinfo_filedescriptor(int fd, list_dump_info_t *simclist_restrict info); /** * get meta information on a list dump on file. * * [ advanced function ] * * Extracts the meta information from a SimCList dump located in a file. * * @param filename filename of the file to fetch from * @param info reference to a dump metainformation structure to fill * @return 0 for success; <0 for failure * * @see list_dump_filedescriptor() */ int list_dump_getinfo_file(const char *simclist_restrict filename, list_dump_info_t *simclist_restrict info); /** * dump the list into an open, writable file descriptor. * * This function "dumps" the list to a persistent storage so it can be * preserved across process terminations. * When called, the file descriptor must be open for writing and positioned * where the serialized data must begin. It writes its serialization of the * list in a form which is portable across different architectures. Dump can * be safely performed on stream-only (non seekable) descriptors. The file * descriptor is not closed at the end of the operations. * * To use dump functions, either of these conditions must be satisfied: * -# a metric function has been specified with list_attributes_copy() * -# a serializer function has been specified with list_attributes_serializer() * * If a metric function has been specified, each element of the list is dumped * as-is from memory, copying it from its pointer for its length down to the * file descriptor. This might have impacts on portability of the dump to * different architectures. * * If a serializer function has been specified, its result for each element is * dumped to the file descriptor. * * * @param l list to operate * @param fd file descriptor to write to * @param len location to store the resulting length of the dump (bytes), or NULL * * @return 0 if successful; -1 otherwise * * @see element_serializer() * @see list_attributes_copy() * @see list_attributes_serializer() */ int list_dump_filedescriptor(const list_t *simclist_restrict l, int fd, size_t *simclist_restrict len); /** * dump the list to a file name. * * This function creates a filename and dumps the current content of the list * to it. If the file exists it is overwritten. The number of bytes written to * the file can be returned in a specified argument. * * @param l list to operate * @param filename filename to write to * @param len location to store the resulting length of the dump (bytes), or NULL * * @return 0 if successful; -1 otherwise * * @see list_attributes_copy() * @see element_serializer() * @see list_attributes_serializer() * @see list_dump_filedescriptor() * @see list_restore_file() * * This function stores a representation of the list */ int list_dump_file(const list_t *simclist_restrict l, const char *simclist_restrict filename, size_t *simclist_restrict len); /** * restore the list from an open, readable file descriptor to memory. * * This function is the "inverse" of list_dump_filedescriptor(). It restores * the list content from a (open, read-ready) file descriptor to memory. An * unserializer might be needed to restore elements from the persistent * representation back into memory-consistent format. List attributes can not * be restored and must be set manually. * * @see list_dump_filedescriptor() * @see list_attributes_serializer() * @see list_attributes_unserializer() * * @param l list to restore to * @param fd file descriptor to read from. * @param len location to store the length of the dump read (bytes), or NULL * @return 0 if successful; -1 otherwise */ int list_restore_filedescriptor(list_t *simclist_restrict l, int fd, size_t *simclist_restrict len); /** * restore the list from a file name. * * This function restores the content of a list from a file into memory. It is * the inverse of list_dump_file(). * * @see element_unserializer() * @see list_attributes_unserializer() * @see list_dump_file() * @see list_restore_filedescriptor() * * @param l list to restore to * @param filename filename to read data from * @param len location to store the length of the dump read (bytes), or NULL * @return 0 if successful; -1 otherwise */ int list_restore_file(list_t *simclist_restrict l, const char *simclist_restrict filename, size_t *len); #endif /* ready-made comparators, meters and hash computers */ /* comparator functions */ /** * ready-made comparator for int8_t elements. * @see list_attributes_comparator() */ int list_comparator_int8_t(const void *a, const void *b); /** * ready-made comparator for int16_t elements. * @see list_attributes_comparator() */ int list_comparator_int16_t(const void *a, const void *b); /** * ready-made comparator for int32_t elements. * @see list_attributes_comparator() */ int list_comparator_int32_t(const void *a, const void *b); /** * ready-made comparator for int64_t elements. * @see list_attributes_comparator() */ int list_comparator_int64_t(const void *a, const void *b); /** * ready-made comparator for uint8_t elements. * @see list_attributes_comparator() */ int list_comparator_uint8_t(const void *a, const void *b); /** * ready-made comparator for uint16_t elements. * @see list_attributes_comparator() */ int list_comparator_uint16_t(const void *a, const void *b); /** * ready-made comparator for uint32_t elements. * @see list_attributes_comparator() */ int list_comparator_uint32_t(const void *a, const void *b); /** * ready-made comparator for uint64_t elements. * @see list_attributes_comparator() */ int list_comparator_uint64_t(const void *a, const void *b); /** * ready-made comparator for float elements. * @see list_attributes_comparator() */ int list_comparator_float(const void *a, const void *b); /** * ready-made comparator for double elements. * @see list_attributes_comparator() */ int list_comparator_double(const void *a, const void *b); /** * ready-made comparator for string elements. * @see list_attributes_comparator() */ int list_comparator_string(const void *a, const void *b); /* metric functions */ /** * ready-made metric function for int8_t elements. * @see list_attributes_copy() */ size_t list_meter_int8_t(const void *el); /** * ready-made metric function for int16_t elements. * @see list_attributes_copy() */ size_t list_meter_int16_t(const void *el); /** * ready-made metric function for int32_t elements. * @see list_attributes_copy() */ size_t list_meter_int32_t(const void *el); /** * ready-made metric function for int64_t elements. * @see list_attributes_copy() */ size_t list_meter_int64_t(const void *el); /** * ready-made metric function for uint8_t elements. * @see list_attributes_copy() */ size_t list_meter_uint8_t(const void *el); /** * ready-made metric function for uint16_t elements. * @see list_attributes_copy() */ size_t list_meter_uint16_t(const void *el); /** * ready-made metric function for uint32_t elements. * @see list_attributes_copy() */ size_t list_meter_uint32_t(const void *el); /** * ready-made metric function for uint64_t elements. * @see list_attributes_copy() */ size_t list_meter_uint64_t(const void *el); /** * ready-made metric function for float elements. * @see list_attributes_copy() */ size_t list_meter_float(const void *el); /** * ready-made metric function for double elements. * @see list_attributes_copy() */ size_t list_meter_double(const void *el); /** * ready-made metric function for string elements. * @see list_attributes_copy() */ size_t list_meter_string(const void *el); /* hash functions */ /** * ready-made hash function for int8_t elements. * @see list_attributes_hash_computer() */ list_hash_t list_hashcomputer_int8_t(const void *el); /** * ready-made hash function for int16_t elements. * @see list_attributes_hash_computer() */ list_hash_t list_hashcomputer_int16_t(const void *el); /** * ready-made hash function for int32_t elements. * @see list_attributes_hash_computer() */ list_hash_t list_hashcomputer_int32_t(const void *el); /** * ready-made hash function for int64_t elements. * @see list_attributes_hash_computer() */ list_hash_t list_hashcomputer_int64_t(const void *el); /** * ready-made hash function for uint8_t elements. * @see list_attributes_hash_computer() */ list_hash_t list_hashcomputer_uint8_t(const void *el); /** * ready-made hash function for uint16_t elements. * @see list_attributes_hash_computer() */ list_hash_t list_hashcomputer_uint16_t(const void *el); /** * ready-made hash function for uint32_t elements. * @see list_attributes_hash_computer() */ list_hash_t list_hashcomputer_uint32_t(const void *el); /** * ready-made hash function for uint64_t elements. * @see list_attributes_hash_computer() */ list_hash_t list_hashcomputer_uint64_t(const void *el); /** * ready-made hash function for float elements. * @see list_attributes_hash_computer() */ list_hash_t list_hashcomputer_float(const void *el); /** * ready-made hash function for double elements. * @see list_attributes_hash_computer() */ list_hash_t list_hashcomputer_double(const void *el); /** * ready-made hash function for string elements. * @see list_attributes_hash_computer() */ list_hash_t list_hashcomputer_string(const void *el); #ifdef __cplusplus } #endif #endif OpenSC-0.26.1/src/libopensc/000077500000000000000000000000001474147347300155125ustar00rootroot00000000000000OpenSC-0.26.1/src/libopensc/Makefile.am000066400000000000000000000135601474147347300175530ustar00rootroot00000000000000include $(top_srcdir)/win32/ltrc.inc MAINTAINERCLEANFILES = $(srcdir)/Makefile.in EXTRA_DIST = Makefile.mak opensc.dll.manifest lib_LTLIBRARIES = libopensc.la 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 card-openpgp.h \ card-eoi.h ccid-types.h reader-tr03119.h \ card-cac-common.h card-cardos-common.h 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) 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 \ 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-emulator-filter.c \ \ muscle.c muscle-filesystem.c \ \ ctbcs.c reader-ctapi.c reader-pcsc.c reader-openct.c reader-tr03119.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-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-esteid2018.c card-idprime.c \ card-edo.c card-nqApplet.c card-skeid.c card-eoi.c card-dtrust.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-pteid.c \ pkcs15-oberthur.c pkcs15-itacns.c pkcs15-gemsafeV1.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-eoi.c pkcs15-dtrust.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 endif libopensc_la_LIBTOOLFLAGS = --tag CC libopensc_static_la_LIBTOOLFLAGS = --tag CC libopensc_la_SOURCES = $(libopensc_la_SOURCES_BASE) \ libopensc.exports libopensc_static_la_SOURCES = $(libopensc_la_SOURCES_BASE) if WIN32 libopensc_la_SOURCES += $(top_builddir)/win32/versioninfo.rc endif libopensc_la_LIBADD = $(OPENPACE_LIBS) $(OPTIONAL_OPENSSL_LIBS) \ $(OPTIONAL_OPENCT_LIBS) $(OPTIONAL_ZLIB_LIBS) \ $(top_builddir)/src/pkcs15init/libpkcs15init.la \ $(top_builddir)/src/scconf/libscconf.la \ $(top_builddir)/src/common/libscdl.la \ $(top_builddir)/src/ui/libnotify.la \ $(top_builddir)/src/ui/libstrings.la \ $(top_builddir)/src/sm/libsmeac.la \ $(top_builddir)/src/common/libcompat.la if WIN32 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" \ -no-undefined if WIN32 # def file required for MS users to build library mylibdir=$(libdir) mylib_DATA=.libs/@WIN_LIBPREFIX@opensc-@OPENSC_LT_OLDEST@.dll.def .libs/@WIN_LIBPREFIX@opensc-@OPENSC_LT_OLDEST@.dll.def: libopensc.la 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 \ \ 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 \ \ 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 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 OpenSC-0.26.1/src/libopensc/Makefile.mak000066400000000000000000000054611474147347300177270ustar00rootroot00000000000000TOPDIR = ..\.. 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 \ 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-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-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-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-esteid2018.obj card-idprime.obj \ card-edo.obj card-nqApplet.obj card-skeid.obj card-eoi.obj card-dtrust.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-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 \ pkcs15-esteid2018.obj pkcs15-idprime.obj pkcs15-nqApplet.obj \ pkcs15-starcos-esign.obj pkcs15-skeid.obj pkcs15-eoi.obj pkcs15-dtrust.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 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 Comctl32.lib Shell32.lib user32.lib advapi32.lib ws2_32.lib shlwapi.lib if EXIST opensc.dll.manifest mt -manifest opensc.dll.manifest -outputresource:opensc.dll;2 opensc_a.lib: $(OBJECTS) lib $(LIBFLAGS) /out:opensc_a.lib $(OBJECTS) OpenSC-0.26.1/src/libopensc/apdu.c000066400000000000000000000533611474147347300166170ustar00rootroot00000000000000/* * apdu.c: basic APDU handling functions * * Copyright (C) 2005 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., 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" #include "asn1.h" /*********************************************************************/ /* low level APDU handling functions */ /*********************************************************************/ /** 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) { size_t ret = 4; switch (apdu->cse) { case SC_APDU_CASE_1: if (proto == SC_PROTO_T0) ret++; break; case SC_APDU_CASE_2_SHORT: ret++; break; case SC_APDU_CASE_2_EXT: ret += (proto == SC_PROTO_T0 ? 1 : 3); break; case SC_APDU_CASE_3_SHORT: ret += 1 + apdu->lc; break; case SC_APDU_CASE_3_EXT: ret += apdu->lc + (proto == SC_PROTO_T0 ? 1 : 3); break; case SC_APDU_CASE_4_SHORT: ret += apdu->lc + (proto != SC_PROTO_T0 ? 2 : 1); break; case SC_APDU_CASE_4_EXT: ret += apdu->lc + (proto == SC_PROTO_T0 ? 1 : 5); break; default: return 0; } return ret; } /** 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) { u8 *p = out; size_t len = sc_apdu_get_length(apdu, proto); if (out == NULL || outlen < len) return SC_ERROR_INVALID_ARGUMENTS; /* CLA, INS, P1 and P2 */ *p++ = apdu->cla; *p++ = apdu->ins; *p++ = apdu->p1; *p++ = apdu->p2; /* case depend part */ switch (apdu->cse) { case SC_APDU_CASE_1: /* T0 needs an additional 0x00 byte */ if (proto == SC_PROTO_T0) *p = (u8)0x00; break; case SC_APDU_CASE_2_SHORT: *p = (u8)apdu->le; break; case SC_APDU_CASE_2_EXT: if (proto == SC_PROTO_T0) /* T0 extended APDUs look just like short APDUs */ *p = (u8)apdu->le; else { /* in case of T1 always use 3 bytes for length */ *p++ = (u8)0x00; *p++ = (u8)(apdu->le >> 8); *p = (u8)apdu->le; } break; case SC_APDU_CASE_3_SHORT: *p++ = (u8)apdu->lc; memcpy(p, apdu->data, apdu->lc); break; case SC_APDU_CASE_3_EXT: if (proto == SC_PROTO_T0) { /* in case of T0 the command is transmitted in chunks * < 255 using the ENVELOPE command ... */ if (apdu->lc > 255) { /* ... so if Lc is greater than 255 bytes * an error has occurred on a higher level */ sc_log(ctx, "invalid Lc length for CASE 3 extended APDU (need ENVELOPE)"); return SC_ERROR_INVALID_ARGUMENTS; } } else { /* in case of T1 always use 3 bytes for length */ *p++ = (u8)0x00; *p++ = (u8)(apdu->lc >> 8); *p++ = (u8)apdu->lc; } memcpy(p, apdu->data, apdu->lc); break; case SC_APDU_CASE_4_SHORT: *p++ = (u8)apdu->lc; memcpy(p, apdu->data, apdu->lc); p += apdu->lc; /* in case of T0 no Le byte is added */ if (proto != SC_PROTO_T0) *p = (u8)apdu->le; break; case SC_APDU_CASE_4_EXT: if (proto == SC_PROTO_T0) { /* again a T0 extended case 4 APDU looks just * like a short APDU, the additional data is * transferred using ENVELOPE and GET RESPONSE */ *p++ = (u8)apdu->lc; memcpy(p, apdu->data, apdu->lc); } else { *p++ = (u8)0x00; *p++ = (u8)(apdu->lc >> 8); *p++ = (u8)apdu->lc; memcpy(p, apdu->data, apdu->lc); p += apdu->lc; /* only 2 bytes are use to specify the length of the * expected data */ *p++ = (u8)(apdu->le >> 8); *p = (u8)apdu->le; } break; } return SC_SUCCESS; } int sc_apdu_get_octets(sc_context_t *ctx, const sc_apdu_t *apdu, u8 **buf, size_t *len, unsigned int proto) { size_t nlen; u8 *nbuf; if (apdu == NULL || buf == NULL || len == NULL) return SC_ERROR_INVALID_ARGUMENTS; /* get the estimated length of encoded APDU */ nlen = sc_apdu_get_length(apdu, proto); if (nlen == 0) return SC_ERROR_INTERNAL; nbuf = malloc(nlen); if (nbuf == NULL) return SC_ERROR_OUT_OF_MEMORY; /* encode the APDU in the buffer */ if (sc_apdu2bytes(ctx, apdu, proto, nbuf, nlen) != SC_SUCCESS) { free(nbuf); return SC_ERROR_INTERNAL; } *buf = nbuf; *len = nlen; return SC_SUCCESS; } int sc_apdu_set_resp(sc_context_t *ctx, sc_apdu_t *apdu, const u8 *buf, size_t len) { if (len < 2) { /* no SW1 SW2 ... something went terrible wrong */ sc_log(ctx, "invalid response: SW1 SW2 missing"); return SC_ERROR_INTERNAL; } /* set the SW1 and SW2 status bytes (the last two bytes of * the response */ apdu->sw1 = (unsigned int)buf[len - 2]; apdu->sw2 = (unsigned int)buf[len - 1]; len -= 2; /* set output length and copy the returned data if necessary */ if (len <= apdu->resplen) apdu->resplen = len; if (apdu->resplen != 0) memcpy(apdu->resp, buf, apdu->resplen); return SC_SUCCESS; } /*********************************************************************/ /* higher level APDU transfer handling functions */ /*********************************************************************/ /* +------------------+ * | sc_transmit_apdu | * +------------------+ * | | | * | | | detect APDU cse +--------------------+ * | | +---------------------------------> | sc_detect_apdu_cse | * | | +--------------------+ * | | check consistency of APDU +--------------------+ * | +------------------------------------> | sc_check_apdu | * | +--------------------+ * | send single APDU +--------------------+ * +---------------------------------------> | sc_transmit | * ^ +--------------------+ * | | * | re-transmit if wrong length | * | or GET RESPONSE | * +-------------------------------+ * | * v * card->reader->ops->transmit */ /** basic consistency check of the sc_apdu_t object * @param ctx sc_context_t object for error messages * @param apdu sc_apdu_t object to check * @return SC_SUCCESS on success and an error code otherwise */ int sc_check_apdu(sc_card_t *card, const sc_apdu_t *apdu) { if ((apdu->cse & ~SC_APDU_SHORT_MASK) == 0) { /* length check for short APDU */ if (apdu->le > 256 || (apdu->lc > 255 && (apdu->flags & SC_APDU_FLAGS_CHAINING) == 0)) { sc_log(card->ctx, "failed length check for short APDU"); goto error; } } else if ((apdu->cse & SC_APDU_EXT) != 0) { /* check if the card supports extended APDUs */ if ((card->caps & SC_CARD_CAP_APDU_EXT) == 0) { sc_log(card->ctx, "card doesn't support extended APDUs"); goto error; } /* length check for extended APDU */ if (apdu->le > 65536 || apdu->lc > 65535) { sc_log(card->ctx, "failed length check for extended APDU"); goto error; } } else { goto error; } switch (apdu->cse & SC_APDU_SHORT_MASK) { case SC_APDU_CASE_1: /* no data is sent or received */ if (apdu->datalen != 0 || apdu->lc != 0 || apdu->le != 0) goto error; break; case SC_APDU_CASE_2_SHORT: /* no data is sent */ if (apdu->datalen != 0 || apdu->lc != 0) goto error; /* data is expected */ if (apdu->resplen == 0 || apdu->resp == NULL) goto error; break; case SC_APDU_CASE_3_SHORT: /* data is sent */ if (apdu->datalen == 0 || apdu->data == NULL || apdu->lc == 0) goto error; /* no data is expected */ if (apdu->le != 0) goto error; /* inconsistent datalen */ if (apdu->datalen != apdu->lc) goto error; break; case SC_APDU_CASE_4_SHORT: /* data is sent */ if (apdu->datalen == 0 || apdu->data == NULL || apdu->lc == 0) goto error; /* data is expected */ if (apdu->resplen == 0 || apdu->resp == NULL) goto error; /* inconsistent datalen */ if (apdu->datalen != apdu->lc) goto error; break; default: sc_log(card->ctx, "Invalid APDU case %d", apdu->cse); return SC_ERROR_INVALID_ARGUMENTS; } return SC_SUCCESS; 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 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->flags); return SC_ERROR_INVALID_ARGUMENTS; } /** Tries to determine the APDU type (short or extended) of the supplied * APDU if one of the SC_APDU_CASE_? types is used. * @param apdu APDU object */ static void 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 maximum for * short APDUs and the card supports extended APDUs * use extended APDUs (unless Lc is greater than * 255 and command chaining is activated) */ if ((apdu->le > 256 || (apdu->lc > 255 && (apdu->flags & SC_APDU_FLAGS_CHAINING) == 0)) && (card->caps & SC_CARD_CAP_APDU_EXT) != 0) btype |= SC_APDU_EXT; apdu->cse = btype; } } static int sc_single_transmit(struct sc_card *card, struct sc_apdu *apdu) { struct sc_context *ctx = card->ctx; int rv; LOG_FUNC_CALLED(ctx); if (card->reader->ops->transmit == NULL) LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED, "cannot transmit APDU"); sc_log(ctx, "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); #ifdef ENABLE_SM if (card->sm_ctx.sm_mode == SM_MODE_TRANSMIT && (apdu->flags & SC_APDU_FLAGS_NO_SM) == 0) { LOG_FUNC_RETURN(ctx, sc_sm_single_transmit(card, apdu)); } #endif /* send APDU to the reader driver */ rv = card->reader->ops->transmit(card->reader, apdu); LOG_TEST_RET(ctx, rv, "unable to transmit APDU"); LOG_FUNC_RETURN(ctx, rv); } static int sc_set_le_and_transmit(struct sc_card *card, struct sc_apdu *apdu, size_t olen) { struct sc_context *ctx = card->ctx; size_t nlen = apdu->sw2 ? (size_t)apdu->sw2 : 256; int rv; LOG_FUNC_CALLED(ctx); /* we cannot re-transmit the APDU with the demanded Le value * as the buffer is too small => error */ if (olen < nlen) LOG_TEST_RET(ctx, SC_ERROR_WRONG_LENGTH, "wrong length: required length exceeds resplen"); /* don't try again if it doesn't work this time */ apdu->flags |= SC_APDU_FLAGS_NO_RETRY_WL; /* set the new expected length */ apdu->resplen = olen; apdu->le = nlen; #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); LOG_TEST_RET(ctx, rv, "cannot re-transmit APDU"); LOG_FUNC_RETURN(ctx, rv); } static int sc_get_response(struct sc_card *card, struct sc_apdu *apdu, size_t olen) { struct sc_context *ctx = card->ctx; size_t le, minlen, buflen; unsigned char *buf; int rv; LOG_FUNC_CALLED(ctx); if (apdu->le == 0) { /* no data is requested => change return value to 0x9000 and ignore the remaining data */ apdu->sw1 = 0x90; apdu->sw2 = 0x00; return SC_SUCCESS; } /* this should _never_ happen */ 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 returns 0x9000, * whatever happens first. */ /* if there are already data in response append a new data to the end of the buffer */ buf = apdu->resp + apdu->resplen; /* read as much data as fits in apdu->resp (i.e. min(apdu->resplen, amount of data available)). */ buflen = olen - apdu->resplen; /* 0x6100 means at least 256 more bytes to read */ le = apdu->sw2 != 0 ? (size_t)apdu->sw2 : 256; /* we try to read at least as much as bytes as promised in the response bytes */ minlen = le; do { 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)); rv = card->ops->get_response(card, &resp_len, resp); if (rv < 0) { #ifdef ENABLE_SM if (resp_len) { sc_log_hex(ctx, "SM response data", resp, resp_len); sc_sm_update_apdu_response(card, resp, resp_len, rv, apdu); } #endif LOG_TEST_RET(ctx, rv, "GET RESPONSE error"); } le = resp_len; /* copy as much as will fit in requested buffer */ if (buflen < le) le = buflen; memcpy(buf, resp, le); buf += le; buflen -= le; minlen -= le; if (rv != 0) le = minlen = (size_t)rv; else /* if the card has returned 0x9000 but we still expect data ask for more * until we have read enough bytes */ le = minlen; } while (rv != 0 && minlen != 0); /* we've read all data, let's return 0x9000 */ apdu->resplen = buf - apdu->resp; apdu->sw1 = 0x90; apdu->sw2 = 0x00; LOG_FUNC_RETURN(ctx, SC_SUCCESS); } /** Sends a single APDU to the card reader and calls GET RESPONSE to get the return data if necessary. * @param card sc_card_t object for the smartcard * @param apdu APDU to be sent * @return SC_SUCCESS on success and an error value otherwise */ static int sc_transmit(sc_card_t *card, sc_apdu_t *apdu) { struct sc_context *ctx = card->ctx; size_t olen = apdu->resplen; int r; LOG_FUNC_CALLED(ctx); r = sc_single_transmit(card, 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-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) { r = sc_set_le_and_transmit(card, apdu, olen); 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) { r = sc_get_response(card, apdu, olen); LOG_TEST_RET(ctx, r, "cannot get all data with 'GET RESPONSE'"); } LOG_FUNC_RETURN(ctx, SC_SUCCESS); } int sc_transmit_apdu(sc_card_t *card, sc_apdu_t *apdu) { int r = SC_SUCCESS; if (card == NULL || apdu == NULL) return SC_ERROR_INVALID_ARGUMENTS; LOG_FUNC_CALLED(card->ctx); /* determine the APDU type if necessary, i.e. to use * short or extended APDUs */ sc_detect_apdu_cse(card, apdu); /* basic APDU consistency check */ r = sc_check_apdu(card, apdu); if (r != SC_SUCCESS) return SC_ERROR_INVALID_ARGUMENTS; r = sc_lock(card); /* acquire card lock*/ if (r != SC_SUCCESS) { sc_log(card->ctx, "unable to acquire lock"); 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 */ size_t len = apdu->datalen; const u8 *buf = apdu->data; size_t max_send_size = sc_get_max_send_size(card); while (len != 0) { size_t plen; sc_apdu_t tapdu; int last = 0; tapdu = *apdu; /* clear chaining flag */ tapdu.flags &= ~SC_APDU_FLAGS_CHAINING; if (len > max_send_size) { /* adjust APDU case: in case of CASE 4 APDU * the intermediate APDU are of CASE 3 */ if ((tapdu.cse & SC_APDU_SHORT_MASK) == SC_APDU_CASE_4_SHORT) tapdu.cse--; /* XXX: the chunk size must be adjusted when * secure messaging is used */ plen = max_send_size; tapdu.cla |= 0x10; /* the intermediate APDU don't expect response data */ tapdu.le = 0; tapdu.resplen = 0; tapdu.resp = NULL; } else { plen = len; last = 1; } tapdu.data = buf; tapdu.datalen = tapdu.lc = plen; r = sc_check_apdu(card, &tapdu); if (r != SC_SUCCESS) { sc_log(card->ctx, "inconsistent APDU while chaining"); break; } r = sc_transmit(card, &tapdu); if (r != SC_SUCCESS) break; if (last != 0) { /* in case of the last APDU set the SW1 * and SW2 bytes in the original APDU */ apdu->sw1 = tapdu.sw1; apdu->sw2 = tapdu.sw2; apdu->resplen = tapdu.resplen; } else { /* otherwise check the status bytes */ r = sc_check_sw(card, tapdu.sw1, tapdu.sw2); if (r != SC_SUCCESS) break; } len -= plen; buf += plen; } } else { /* transmit single APDU */ r = sc_transmit(card, apdu); } if (r == SC_ERROR_CARD_RESET || r == SC_ERROR_READER_REATTACHED) { sc_invalidate_cache(card); /* 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"); return r; } int sc_bytes2apdu(sc_context_t *ctx, const u8 *buf, size_t len, sc_apdu_t *apdu) { const unsigned char *p; size_t len0; if (!buf || !apdu) return SC_ERROR_INVALID_ARGUMENTS; len0 = len; if (len < 4) { sc_log(ctx, "APDU too short (must be at least 4 bytes)"); return SC_ERROR_INVALID_DATA; } memset(apdu, 0, sizeof *apdu); p = buf; apdu->cla = *p++; apdu->ins = *p++; apdu->p1 = *p++; apdu->p2 = *p++; len -= 4; if (!len) { apdu->cse = SC_APDU_CASE_1; sc_log(ctx, "CASE_1 APDU: %"SC_FORMAT_LEN_SIZE_T"u bytes:\tins=%02x p1=%02x p2=%02x lc=%04"SC_FORMAT_LEN_SIZE_T"x le=%04"SC_FORMAT_LEN_SIZE_T"x", len0, apdu->ins, apdu->p1, apdu->p2, apdu->lc, apdu->le); return SC_SUCCESS; } if (*p == 0 && len >= 3) { /* ...must be an extended APDU */ p++; if (len == 3) { apdu->le = (*p++)<<8; apdu->le += *p++; if (apdu->le == 0) apdu->le = 0xffff+1; len -= 3; apdu->cse = SC_APDU_CASE_2_EXT; } else { /* len > 3 */ apdu->lc = (*p++)<<8; apdu->lc += *p++; len -= 3; if (len < apdu->lc) { sc_log(ctx, "APDU too short (need %"SC_FORMAT_LEN_SIZE_T"u more bytes)", apdu->lc - len); return SC_ERROR_INVALID_DATA; } apdu->data = p; apdu->datalen = apdu->lc; len -= apdu->lc; p += apdu->lc; if (!len) { apdu->cse = SC_APDU_CASE_3_EXT; } else { /* at this point the apdu has a Lc, so Le is on 2 bytes */ if (len < 2) { sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, "APDU too short (need 2 more bytes)\n"); return SC_ERROR_INVALID_DATA; } apdu->le = (*p++)<<8; apdu->le += *p++; if (apdu->le == 0) apdu->le = 0xffff+1; len -= 2; apdu->cse = SC_APDU_CASE_4_EXT; } } } else { /* ...must be a short APDU */ if (len == 1) { apdu->le = *p++; if (apdu->le == 0) apdu->le = 0xff+1; len--; apdu->cse = SC_APDU_CASE_2_SHORT; } else { apdu->lc = *p++; len--; if (len < apdu->lc) { sc_log(ctx, "APDU too short (need %"SC_FORMAT_LEN_SIZE_T"u more bytes)", apdu->lc - len); return SC_ERROR_INVALID_DATA; } apdu->data = p; apdu->datalen = apdu->lc; len -= apdu->lc; p += apdu->lc; if (!len) { apdu->cse = SC_APDU_CASE_3_SHORT; } else { apdu->le = *p++; if (apdu->le == 0) apdu->le = 0xff+1; len--; apdu->cse = SC_APDU_CASE_4_SHORT; } } } if (len) { sc_log(ctx, "APDU too long (%lu bytes extra)",(unsigned long) len); return SC_ERROR_INVALID_DATA; } sc_log(ctx, "Case %d %s APDU, %"SC_FORMAT_LEN_SIZE_T"u bytes:\tins=%02x p1=%02x p2=%02x lc=%04"SC_FORMAT_LEN_SIZE_T"x le=%04"SC_FORMAT_LEN_SIZE_T"x", apdu->cse & SC_APDU_SHORT_MASK, (apdu->cse & SC_APDU_EXT) != 0 ? "extended" : "short", len0, apdu->ins, apdu->p1, apdu->p2, apdu->lc, apdu->le); return SC_SUCCESS; } OpenSC-0.26.1/src/libopensc/asn1.c000066400000000000000000001711551474147347300165320ustar00rootroot00000000000000/* * asn1.c: ASN.1 decoding functions (DER) * * Copyright (C) 2001, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include "internal.h" #include "asn1.h" 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); static int asn1_encode(sc_context_t *ctx, const struct sc_asn1_entry *asn1, u8 **ptr, size_t *size, int depth); static int asn1_write_element(sc_context_t *ctx, unsigned int tag, const u8 * data, size_t datalen, u8 ** out, size_t * outlen); static const char *tag2str(unsigned int tag) { static const char *tags[] = { "EOC", "BOOLEAN", "INTEGER", "BIT STRING", "OCTET STRING", /* 0-4 */ "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) return "(unknown)"; return tags[tag]; } int sc_asn1_read_tag(const u8 ** buf, size_t buflen, unsigned int *cla_out, unsigned int *tag_out, size_t *taglen) { const u8 *p = *buf; size_t left = buflen, len; unsigned int cla, tag, i; *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 */ 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; p++; left--; n--; } while (tag & 0x80); } /* parse length byte(s) */ if (left == 0) return SC_ERROR_INVALID_ASN1_OBJECT; len = *p; p++; left--; if (len & 0x80) { len &= 0x7f; unsigned int a = 0; if (len > sizeof a || len > left) return SC_ERROR_INVALID_ASN1_OBJECT; for (i = 0; i < len; i++) { a <<= 8; a |= *p; p++; left--; } len = a; } *cla_out = cla; *tag_out = tag; *taglen = len; *buf = p; if (len > left) return SC_ERROR_ASN1_END_OF_CONTENTS; return SC_SUCCESS; } void sc_format_asn1_entry(struct sc_asn1_entry *entry, void *parm, void *arg, int set_present) { entry->parm = parm; entry->arg = arg; if (set_present) entry->flags |= SC_ASN1_PRESENT; } void sc_copy_asn1_entry(const struct sc_asn1_entry *src, struct sc_asn1_entry *dest) { while (src->name != NULL) { *dest = *src; dest++; src++; } dest->name = NULL; } static void print_indent(size_t depth) { 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; } free(lines); } static void print_ascii(const u8 * buf, size_t buflen) { for (; 0 < buflen; buflen--, buf++) { if (isprint(*buf)) printf("%c", *buf); else putchar('.'); } } 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) { size_t a = 0; if (buflen > sizeof(a)) { 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); } } static void sc_asn1_print_boolean(const u8 * buf, size_t buflen) { if (!buflen) return; if (buf[0]) printf("true"); else printf("false"); } static void sc_asn1_print_bit_string(const u8 * buf, size_t buflen, size_t depth) { #ifndef _WIN32 long long a = 0; #else __int64 a = 0; #endif int r, i; if (buflen > sizeof(a) + 1) { 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'); } } } #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; const char *sbuf; if (sc_asn1_decode_object_id(buf, buflen, &oid)) { printf("decode error"); return; } 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; } 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 */ } } static void sc_asn1_print_generalizedtime(const u8 * buf, size_t buflen) { if (buflen < 8) { printf("Error in decoding.\n"); return; } print_ascii(buf, 2); sc_asn1_print_utctime(buf + 2, buflen - 2); } static void print_tags_recursive(const u8 * buf0, const u8 * buf, size_t buflen, size_t depth) { int r; size_t i; size_t bytesleft = buflen; const char *classes[4] = { "Universal", "Application", "Context", "Private" }; const u8 *p = buf; while (bytesleft >= 2) { 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 || (tagp == NULL && tag != SC_ASN1_TAG_EOC)) { printf("Error in decoding.\n"); return; } hlen = tagp - p; if (cla == 0 && tag == 0) { printf("Zero tag, finishing\n"); break; } 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)); } 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_CONSTRUCTED) { putchar('\n'); print_tags_recursive(buf0, tagp, len, depth + 2*i + 1); continue; } switch (tag) { case SC_ASN1_TAG_BIT_STRING: 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, 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_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; case SC_ASN1_UTCTIME: printf(": "); sc_asn1_print_utctime(tagp, len); break; } 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) { print_hex(tagp, len, depth + 2*i + 1); } putchar('\n'); } } void sc_asn1_print_tags(const u8 * buf, size_t buflen) { print_tags_recursive(buf, buf, buflen, 0); } const u8 *sc_asn1_find_tag(sc_context_t *ctx, const u8 * buf, size_t buflen, unsigned int tag_in, size_t *taglen_in) { size_t left = buflen, taglen; const u8 *p = buf; *taglen_in = 0; while (left >= 2) { unsigned int cla = 0, tag, mask = 0xff00; buf = p; /* read a tag */ 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 */ while ((tag & mask) != 0) { cla <<= 8; mask <<= 8; } /* compare the read tag with the given tag */ if ((tag | cla) == tag_in) { /* we have a match => return length and value part */ if (taglen > left) return NULL; *taglen_in = taglen; return p; } /* otherwise continue reading tags */ left -= taglen; p += taglen; } return NULL; } const u8 *sc_asn1_skip_tag(sc_context_t *ctx, const u8 ** buf, size_t *buflen, unsigned int tag_in, size_t *taglen_out) { const u8 *p = *buf; size_t len = *buflen, taglen; unsigned int cla = 0, tag; 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: if ((tag_in & SC_ASN1_CLASS_MASK) != SC_ASN1_UNI) return NULL; break; case SC_ASN1_TAG_APPLICATION: if ((tag_in & SC_ASN1_CLASS_MASK) != SC_ASN1_APP) return NULL; break; case SC_ASN1_TAG_CONTEXT: if ((tag_in & SC_ASN1_CLASS_MASK) != SC_ASN1_CTX) return NULL; break; case SC_ASN1_TAG_PRIVATE: if ((tag_in & SC_ASN1_CLASS_MASK) != SC_ASN1_PRV) return NULL; break; } if (cla & SC_ASN1_TAG_CONSTRUCTED) { if ((tag_in & SC_ASN1_CONS) == 0) return NULL; } else if (tag_in & SC_ASN1_CONS) return NULL; if ((tag_in & SC_ASN1_TAG_MASK) != tag) return NULL; len -= (p - *buf); /* header size */ if (taglen > len) { sc_debug(ctx, SC_LOG_DEBUG_ASN1, "too long ASN.1 object (size %"SC_FORMAT_LEN_SIZE_T"u while only %"SC_FORMAT_LEN_SIZE_T"u available)\n", taglen, len); return NULL; } *buflen -= (p - *buf) + taglen; *buf = p + taglen; /* point to next tag */ *taglen_out = taglen; return p; } const u8 *sc_asn1_verify_tag(sc_context_t *ctx, const u8 * buf, size_t buflen, unsigned int tag_in, size_t *taglen_out) { return sc_asn1_skip_tag(ctx, &buf, &buflen, tag_in, taglen_out); } static int decode_bit_string(const u8 * inbuf, size_t inlen, void *outbuf, size_t outlen, int invert, const int strict) { const u8 *in = inbuf; u8 *out = (u8 *) outbuf; 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; while (octets_left) { /* 1st octet of input: ABCDEFGH, where A is the MSB */ /* 1st octet of output: HGFEDCBA, where A is the LSB */ /* first bit in bit string is the LSB in first resulting octet */ int bits_to_go; *out = 0; if (octets_left == 1 && zero_bits > 0) { bits_to_go = 8 - zero_bits; /* 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++) { *out |= ((*in >> (7 - i)) & 1) << i; } else { *out = *in; } out++; in++; octets_left--; count++; } return (count * 8) - zero_bits; } int sc_asn1_decode_bit_string(const u8 * inbuf, size_t inlen, void *outbuf, size_t outlen, const int strict) { 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, const int strict) { return decode_bit_string(inbuf, inlen, outbuf, outlen, 0, strict); } static int encode_bit_string(const u8 * inbuf, size_t bits_left, u8 **outbuf, size_t *outlen, int invert) { const u8 *in = inbuf; u8 *out; size_t bytes, skipped = 0; bytes = (bits_left + 7)/8 + 1; *outbuf = out = malloc(bytes); if (out == NULL) return SC_ERROR_OUT_OF_MEMORY; *outlen = bytes; out += 1; while (bits_left) { size_t i, bits_to_go = 8; *out = 0; if (bits_left < 8) { bits_to_go = bits_left; skipped = 8 - bits_left; } if (invert) { for (i = 0; i < bits_to_go; i++) *out |= ((*in >> i) & 1) << (7 - i); } else { *out = *in; if (bits_left < 8) return SC_ERROR_NOT_SUPPORTED; /* FIXME */ } bits_left -= bits_to_go; out++, in++; } out = *outbuf; out[0] = skipped; return 0; } /* * 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, const int strict) { u8 data[sizeof(unsigned int)]; unsigned int field = 0; int i, n; if (outlen != sizeof(data)) return SC_ERROR_BUFFER_TOO_SMALL; n = decode_bit_string(inbuf, inlen, data, sizeof(data), 1, strict); if (n < 0) return n; for (i = 0; i < n; i += 8) { field |= ((unsigned int) data[i/8] << i); } memcpy(outbuf, &field, outlen); return 0; } static int encode_bit_field(const u8 *inbuf, size_t inlen, u8 **outbuf, size_t *outlen) { u8 data[sizeof(unsigned int)]; unsigned int field = 0; size_t i, bits; if (inlen != sizeof(data)) return SC_ERROR_BUFFER_TOO_SMALL; /* count the bits */ memcpy(&field, inbuf, inlen); for (bits = 0; field; bits++) field >>= 1; memcpy(&field, inbuf, inlen); for (i = 0; i < bits; i += 8) data[i/8] = field >> i; return encode_bit_string(data, bits, outbuf, outlen, 1); } int sc_asn1_decode_integer(const u8 * inbuf, size_t inlen, int *out, int strict) { int a = 0, is_negative = 0; size_t i = 0; if (inlen == 0) { return SC_ERROR_INVALID_ASN1_OBJECT; } 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; 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; } static int asn1_encode_integer(int in, u8 ** obj, size_t * objsize) { int i = sizeof(in) * 8, skip_zero, skip_sign; u8 *p, b; if (in < 0) { skip_sign = 1; skip_zero= 0; } else { skip_sign = 0; skip_zero= 1; } *obj = p = malloc(sizeof(in)+1); if (*obj == NULL) return SC_ERROR_OUT_OF_MEMORY; do { i -= 8; b = in >> i; if (skip_sign) { if (b != 0xff) skip_sign = 0; if (b & 0x80) { *p = b; if (0xff == b) continue; } else { p++; skip_sign = 0; } } if (b == 0 && skip_zero) continue; if (skip_zero) { skip_zero = 0; /* prepend 0x00 if MSb is 1 and integer positive */ if ((b & 0x80) != 0 && in > 0) *p++ = 0; } *p++ = b; } while (i > 0); if (skip_sign) p++; *objsize = p - *obj; if (*objsize == 0) { *objsize = 1; (*obj)[0] = 0; } return 0; } int sc_asn1_decode_object_id(const u8 *inbuf, size_t inlen, struct sc_object_id *id) { int large_second_octet = 0; unsigned int a = 0; const u8 *p = inbuf; int *octet; if (inlen == 0 || inbuf == NULL || id == NULL) return SC_ERROR_INVALID_ARGUMENTS; sc_init_oid(id); octet = id->value; /* 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) { 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; } int sc_asn1_encode_object_id(u8 **buf, size_t *buflen, const struct sc_object_id *id) { u8 temp[SC_MAX_OBJECT_ID_OCTETS*5], *p = temp; int i; if (!buflen || !id) return SC_ERROR_INVALID_ARGUMENTS; /* an OID must have at least two components */ if (id->value[0] == -1 || id->value[1] == -1) return SC_ERROR_INVALID_ARGUMENTS; for (i = 0; i < SC_MAX_OBJECT_ID_OCTETS; i++) { unsigned int k, shift; if (id->value[i] == -1) break; k = id->value[i]; switch (i) { case 0: if (k > 2) return SC_ERROR_INVALID_ARGUMENTS; *p = k * 40; break; case 1: if (k > 39 && id->value[0] < 2) { return SC_ERROR_INVALID_ARGUMENTS; } /* 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) shift -= 7; while (shift) { *p++ = 0x80 | ((k >> shift) & 0x7f); shift -= 7; } *p++ = k & 0x7F; break; } } *buflen = p - temp; if (buf) { *buf = malloc(*buflen); if (!*buf) return SC_ERROR_OUT_OF_MEMORY; memcpy(*buf, temp, *buflen); } return 0; } static int sc_asn1_decode_utf8string(const u8 *inbuf, size_t inlen, u8 *out, size_t *outlen) { if (inlen+1 > *outlen) return SC_ERROR_BUFFER_TOO_SMALL; *outlen = inlen+1; memcpy(out, inbuf, inlen); out[inlen] = 0; 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; unsigned int tag_len, ii; u8 *p = out; u8 tag_char[4] = {0, 0, 0, 0}; /* Check tag */ if (tag == 0 || tag > 0xFFFFFFFF) { /* A tag of 0x00 is not valid and at most 4-byte tag names are supported. */ return SC_ERROR_INVALID_DATA; } for (tag_len = 0; tag; tag >>= 8) { /* Note: tag char will be reversed order. */ tag_char[tag_len++] = tag & 0xFF; } if (tag_len > 1) { if ((tag_char[tag_len - 1] & SC_ASN1_TAG_PRIMITIVE) != SC_ASN1_TAG_ESCAPE_MARKER) { /* First byte is not escape marker. */ return SC_ERROR_INVALID_DATA; } for (ii = 1; ii < tag_len - 1; ii++) { if ((tag_char[ii] & 0x80) != 0x80) { /* MS bit is not 'one'. */ return SC_ERROR_INVALID_DATA; } } if ((tag_char[0] & 0x80) != 0x00) { /* MS bit of the last byte is not 'zero'. */ return SC_ERROR_INVALID_DATA; } } /* Calculate the number of additional bytes necessary to encode the length. */ /* c+1 is the size of the length field. */ if (datalen > 127) { c = 1; while (datalen >> (c << 3)) c++; } if (outlen == 0 || out == NULL) { /* Caller only asks for the length that would be written. */ return (int)(tag_len + (c + 1) + datalen); } /* We will write the tag, so check the length. */ if (outlen < tag_len + (c+1) + datalen) return SC_ERROR_BUFFER_TOO_SMALL; for (ii=0;ii 0) { *p++ = 0x80 | c; while (c--) *p++ = (datalen >> (c << 3)) & 0xFF; } else { *p++ = datalen & 0x7F; } if(data && datalen > 0) { memcpy(p, data, datalen); p += datalen; } if (ptr != NULL) *ptr = p; return 0; } int sc_asn1_write_element(sc_context_t *ctx, unsigned int tag, const u8 * data, size_t datalen, u8 ** out, size_t * outlen) { return asn1_write_element(ctx, tag, data, datalen, out, outlen); } static int asn1_write_element(sc_context_t *ctx, unsigned int tag, const u8 * data, size_t datalen, u8 ** out, size_t * outlen) { unsigned char t; unsigned char *buf, *p; int c = 0; unsigned short_tag; unsigned char tag_char[3] = {0, 0, 0}; size_t tag_len, ii; short_tag = tag & SC_ASN1_TAG_MASK; for (tag_len = 0; short_tag >> (8 * tag_len); tag_len++) tag_char[tag_len] = (short_tag >> (8 * tag_len)) & 0xFF; if (!tag_len) tag_len = 1; if (tag_len > 1) { if ((tag_char[tag_len - 1] & SC_ASN1_TAG_PRIMITIVE) != SC_ASN1_TAG_ESCAPE_MARKER) SC_TEST_RET(ctx, SC_LOG_DEBUG_ASN1, SC_ERROR_INVALID_DATA, "First byte of the long tag is not 'escape marker'"); for (ii = 1; ii < tag_len - 1; ii++) if (!(tag_char[ii] & 0x80)) SC_TEST_RET(ctx, SC_LOG_DEBUG_ASN1, SC_ERROR_INVALID_DATA, "MS bit expected to be 'one'"); if (tag_char[0] & 0x80) SC_TEST_RET(ctx, SC_LOG_DEBUG_ASN1, SC_ERROR_INVALID_DATA, "MS bit of the last byte expected to be 'zero'"); } t = tag_char[tag_len - 1] & 0x1F; switch (tag & SC_ASN1_CLASS_MASK) { case SC_ASN1_UNI: break; case SC_ASN1_APP: t |= SC_ASN1_TAG_APPLICATION; break; case SC_ASN1_CTX: t |= SC_ASN1_TAG_CONTEXT; break; case SC_ASN1_PRV: t |= SC_ASN1_TAG_PRIVATE; break; } if (tag & SC_ASN1_CONS) t |= SC_ASN1_TAG_CONSTRUCTED; if (datalen > 127) { c = 1; while (datalen >> (c << 3)) c++; } *outlen = tag_len + 1 + c + datalen; buf = malloc(*outlen); if (buf == NULL) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_ASN1, SC_ERROR_OUT_OF_MEMORY); *out = p = buf; *p++ = t; for (ii=1;ii> (c << 3)) & 0xFF; } else { *p++ = datalen & 0x7F; } if (datalen && data) { memcpy(p, data, datalen); } return SC_SUCCESS; } static const struct sc_asn1_entry c_asn1_path_ext[3] = { { "aid", SC_ASN1_OCTET_STRING, SC_ASN1_APP | 0x0F, 0, NULL, NULL }, { "path", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_OCTET_STRING, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; static const struct sc_asn1_entry c_asn1_path[5] = { { "path", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_OCTET_STRING, SC_ASN1_OPTIONAL, NULL, NULL }, { "index", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, SC_ASN1_OPTIONAL, NULL, NULL }, { "length", SC_ASN1_INTEGER, SC_ASN1_CTX | 0, SC_ASN1_OPTIONAL, NULL, NULL }, /* For some multi-applications PKCS#15 card the ODF records can hold the references to * the xDF files and objects placed elsewhere then under the application DF of the ODF itself. * In such a case the 'path' ASN1 data includes also the ID of the target application (AID). * This path extension do not make a part of PKCS#15 standard. */ { "pathExtended", SC_ASN1_STRUCT, SC_ASN1_CTX | 1 | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; static int asn1_decode_path(sc_context_t *ctx, const u8 *in, size_t len, sc_path_t *path, int depth) { int idx, count, r; struct sc_asn1_entry asn1_path_ext[3], asn1_path[5]; unsigned char path_value[SC_MAX_PATH_SIZE], aid_value[SC_MAX_AID_SIZE]; size_t path_len = sizeof(path_value), aid_len = sizeof(aid_value); memset(path, 0, sizeof(struct sc_path)); sc_copy_asn1_entry(c_asn1_path_ext, asn1_path_ext); sc_copy_asn1_entry(c_asn1_path, asn1_path); sc_format_asn1_entry(asn1_path_ext + 0, aid_value, &aid_len, 0); sc_format_asn1_entry(asn1_path_ext + 1, path_value, &path_len, 0); sc_format_asn1_entry(asn1_path + 0, path_value, &path_len, 0); sc_format_asn1_entry(asn1_path + 1, &idx, NULL, 0); sc_format_asn1_entry(asn1_path + 2, &count, NULL, 0); sc_format_asn1_entry(asn1_path + 3, asn1_path_ext, NULL, 0); r = asn1_decode(ctx, asn1_path, in, len, NULL, NULL, 0, depth + 1); if (r) return r; if (asn1_path[3].flags & SC_ASN1_PRESENT) { /* extended path present: set 'path' and 'aid' */ memcpy(path->aid.value, aid_value, aid_len); path->aid.len = aid_len; memcpy(path->value, path_value, path_len); path->len = path_len; } else if (asn1_path[0].flags & SC_ASN1_PRESENT) { /* path present: set 'path' */ memcpy(path->value, path_value, path_len); path->len = path_len; } else { /* failed if both 'path' and 'pathExtended' are absent */ return SC_ERROR_ASN1_OBJECT_NOT_FOUND; } if (path->len == 2) path->type = SC_PATH_TYPE_FILE_ID; else if (path->aid.len && path->len > 2) path->type = SC_PATH_TYPE_FROM_CURRENT; else path->type = SC_PATH_TYPE_PATH; if ((asn1_path[1].flags & SC_ASN1_PRESENT) && (asn1_path[2].flags & SC_ASN1_PRESENT)) { path->index = idx; path->count = count; } else { path->index = 0; path->count = -1; } return SC_SUCCESS; } static int asn1_encode_path(sc_context_t *ctx, const sc_path_t *path, u8 **buf, size_t *bufsize, int depth, unsigned int parent_flags) { int r; struct sc_asn1_entry asn1_path[5]; sc_path_t tpath = *path; sc_copy_asn1_entry(c_asn1_path, asn1_path); sc_format_asn1_entry(asn1_path + 0, (void *) &tpath.value, (void *) &tpath.len, 1); asn1_path[0].flags |= parent_flags; if (path->count > 0) { sc_format_asn1_entry(asn1_path + 1, (void *) &tpath.index, NULL, 1); sc_format_asn1_entry(asn1_path + 2, (void *) &tpath.count, NULL, 1); } r = asn1_encode(ctx, asn1_path, buf, bufsize, depth + 1); return r; } static const struct sc_asn1_entry c_asn1_se[2] = { { "seInfo", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; static const struct sc_asn1_entry c_asn1_se_info[4] = { { "se", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, { "owner",SC_ASN1_OBJECT, SC_ASN1_TAG_OBJECT, SC_ASN1_OPTIONAL, NULL, NULL }, { "aid", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_OCTET_STRING, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; static int asn1_decode_se_info(sc_context_t *ctx, const u8 *obj, size_t objlen, sc_pkcs15_sec_env_info_t ***se, size_t *num, int depth) { struct sc_pkcs15_sec_env_info **ses; const unsigned char *ptr = obj; 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) { 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]; struct sc_asn1_entry asn1_se_info[4]; struct sc_pkcs15_sec_env_info si; sc_copy_asn1_entry(c_asn1_se, asn1_se); sc_copy_asn1_entry(c_asn1_se_info, asn1_se_info); si.aid.len = sizeof(si.aid.value); sc_format_asn1_entry(asn1_se_info + 0, &si.se, NULL, 0); sc_format_asn1_entry(asn1_se_info + 1, &si.owner, NULL, 0); sc_format_asn1_entry(asn1_se_info + 2, &si.aid.value, &si.aid.len, 0); sc_format_asn1_entry(asn1_se + 0, asn1_se_info, NULL, 0); ret = asn1_decode(ctx, asn1_se, ptr, ptrlen, &ptr, &ptrlen, 0, depth+1); if (ret != SC_SUCCESS) goto err; if (!(asn1_se_info[1].flags & SC_ASN1_PRESENT)) sc_init_oid(&si.owner); ses[idx] = calloc(1, sizeof(sc_pkcs15_sec_env_info_t)); if (ses[idx] == NULL) { ret = SC_ERROR_OUT_OF_MEMORY; goto err; } memcpy(ses[idx], &si, sizeof(struct sc_pkcs15_sec_env_info)); idx++; } *se = ses; *num = idx; ret = SC_SUCCESS; err: if (ret != SC_SUCCESS) { size_t i; for (i = 0; i < idx; i++) if (ses[i]) free(ses[i]); free(ses); } SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_ASN1, ret); } static int asn1_encode_se_info(sc_context_t *ctx, struct sc_pkcs15_sec_env_info **se, size_t se_num, unsigned char **buf, size_t *bufsize, int depth) { unsigned char *ptr = NULL, *out = NULL, *p; size_t ptrlen = 0, outlen = 0, idx; int ret; for (idx=0; idx < se_num; idx++) { struct sc_asn1_entry asn1_se[2]; struct sc_asn1_entry asn1_se_info[4]; sc_copy_asn1_entry(c_asn1_se, asn1_se); sc_copy_asn1_entry(c_asn1_se_info, asn1_se_info); sc_format_asn1_entry(asn1_se_info + 0, &se[idx]->se, NULL, 1); if (sc_valid_oid(&se[idx]->owner)) sc_format_asn1_entry(asn1_se_info + 1, &se[idx]->owner, NULL, 1); if (se[idx]->aid.len) sc_format_asn1_entry(asn1_se_info + 2, &se[idx]->aid.value, &se[idx]->aid.len, 1); sc_format_asn1_entry(asn1_se + 0, asn1_se_info, NULL, 1); ret = sc_asn1_encode(ctx, asn1_se, &ptr, &ptrlen); if (ret != SC_SUCCESS) goto err; if (!ptrlen) continue; p = (unsigned char *) realloc(out, outlen + ptrlen); if (!p) { ret = SC_ERROR_OUT_OF_MEMORY; goto err; } out = p; memcpy(out + outlen, ptr, ptrlen); outlen += ptrlen; free(ptr); ptr = NULL; ptrlen = 0; } *buf = out; *bufsize = outlen; ret = SC_SUCCESS; err: if (ret != SC_SUCCESS && out != NULL) free(out); return ret; } /* TODO: According to specification type of 'SecurityCondition' is 'CHOICE'. * Do it at least for SC_ASN1_PKCS15_ID(authId), SC_ASN1_STRUCT(authReference) and NULL(always). */ static const struct sc_asn1_entry c_asn1_access_control_rule[3] = { { "accessMode", SC_ASN1_BIT_FIELD, SC_ASN1_TAG_BIT_STRING, SC_ASN1_OPTIONAL, NULL, NULL }, { "securityCondition", SC_ASN1_PKCS15_ID, SC_ASN1_TAG_OCTET_STRING, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; /* * in src/libopensc/pkcs15.h SC_PKCS15_MAX_ACCESS_RULES defined as 8 */ static const struct sc_asn1_entry c_asn1_access_control_rules[SC_PKCS15_MAX_ACCESS_RULES + 1] = { { "accessControlRule", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { "accessControlRule", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { "accessControlRule", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { "accessControlRule", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { "accessControlRule", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { "accessControlRule", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { "accessControlRule", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { "accessControlRule", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; static const struct sc_asn1_entry c_asn1_com_obj_attr[6] = { { "label", SC_ASN1_UTF8STRING, SC_ASN1_TAG_UTF8STRING, SC_ASN1_OPTIONAL, NULL, NULL }, { "flags", SC_ASN1_BIT_FIELD, SC_ASN1_TAG_BIT_STRING, SC_ASN1_OPTIONAL, NULL, NULL }, { "authId", SC_ASN1_PKCS15_ID, SC_ASN1_TAG_OCTET_STRING, SC_ASN1_OPTIONAL, NULL, NULL }, { "userConsent", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, SC_ASN1_OPTIONAL, NULL, NULL }, { "accessControlRules", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; static const struct sc_asn1_entry c_asn1_p15_obj[5] = { { "commonObjectAttributes", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, { "classAttributes", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, { "subClassAttributes", SC_ASN1_STRUCT, SC_ASN1_CTX | 0 | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { "typeAttributes", SC_ASN1_STRUCT, SC_ASN1_CTX | 1 | SC_ASN1_CONS, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; static int asn1_decode_p15_object(sc_context_t *ctx, const u8 *in, size_t len, struct sc_asn1_pkcs15_object *obj, int depth) { struct sc_pkcs15_object *p15_obj = obj->p15_obj; struct sc_asn1_entry asn1_c_attr[6], asn1_p15_obj[5]; struct sc_asn1_entry asn1_ac_rules[SC_PKCS15_MAX_ACCESS_RULES + 1], asn1_ac_rule[SC_PKCS15_MAX_ACCESS_RULES][3]; size_t flags_len = sizeof(p15_obj->flags); size_t label_len = sizeof(p15_obj->label); size_t access_mode_len = sizeof(p15_obj->access_rules[0].access_mode); int r, ii; for (ii=0; iilabel, &label_len, 0); sc_format_asn1_entry(asn1_c_attr + 1, &p15_obj->flags, &flags_len, 0); sc_format_asn1_entry(asn1_c_attr + 2, &p15_obj->auth_id, NULL, 0); sc_format_asn1_entry(asn1_c_attr + 3, &p15_obj->user_consent, NULL, 0); for (ii=0; iiaccess_rules[ii].access_mode, &access_mode_len, 0); sc_format_asn1_entry(asn1_ac_rule[ii] + 1, &p15_obj->access_rules[ii].auth_id, NULL, 0); sc_format_asn1_entry(asn1_ac_rules + ii, asn1_ac_rule[ii], NULL, 0); } sc_format_asn1_entry(asn1_c_attr + 4, asn1_ac_rules, NULL, 0); sc_format_asn1_entry(asn1_p15_obj + 0, asn1_c_attr, NULL, 0); sc_format_asn1_entry(asn1_p15_obj + 1, obj->asn1_class_attr, NULL, 0); sc_format_asn1_entry(asn1_p15_obj + 2, obj->asn1_subclass_attr, NULL, 0); sc_format_asn1_entry(asn1_p15_obj + 3, obj->asn1_type_attr, NULL, 0); r = asn1_decode(ctx, asn1_p15_obj, in, len, NULL, NULL, 0, depth + 1); return r; } static int asn1_encode_p15_object(sc_context_t *ctx, const struct sc_asn1_pkcs15_object *obj, u8 **buf, size_t *bufsize, int depth) { struct sc_pkcs15_object p15_obj = *obj->p15_obj; struct sc_asn1_entry asn1_c_attr[6], asn1_p15_obj[5]; struct sc_asn1_entry asn1_ac_rules[SC_PKCS15_MAX_ACCESS_RULES + 1], asn1_ac_rule[SC_PKCS15_MAX_ACCESS_RULES][3]; size_t label_len = strlen(p15_obj.label); size_t flags_len; size_t access_mode_len; int r, ii; sc_debug(ctx, SC_LOG_DEBUG_ASN1, "encode p15 obj(type:0x%X,access_mode:0x%X)", p15_obj.type, p15_obj.access_rules[0].access_mode); if (p15_obj.access_rules[0].access_mode) { for (ii=0; iiasn1_class_attr, NULL, 1); if (obj->asn1_subclass_attr != NULL && obj->asn1_subclass_attr->name) sc_format_asn1_entry(asn1_p15_obj + 2, obj->asn1_subclass_attr, NULL, 1); sc_format_asn1_entry(asn1_p15_obj + 3, obj->asn1_type_attr, NULL, 1); r = asn1_encode(ctx, asn1_p15_obj, buf, bufsize, depth + 1); return r; } static int asn1_decode_entry(sc_context_t *ctx,struct sc_asn1_entry *entry, const u8 *obj, size_t objlen, int depth) { void *parm = entry->parm; int (*callback_func)(sc_context_t *nctx, void *arg, const u8 *nobj, size_t nobjlen, int ndepth); size_t *len = (size_t *) entry->arg; int r = 0; callback_func = parm; sc_debug(ctx, SC_LOG_DEBUG_ASN1, "%*.*sdecoding '%s', raw data:%s%s\n", depth, depth, "", entry->name, sc_dump_hex(obj, objlen > 16 ? 16 : objlen), objlen > 16 ? "..." : ""); switch (entry->type) { case SC_ASN1_STRUCT: if (parm != NULL) r = asn1_decode(ctx, (struct sc_asn1_entry *) parm, obj, objlen, NULL, NULL, 0, depth + 1); break; case SC_ASN1_NULL: break; case SC_ASN1_BOOLEAN: if (parm != NULL) { if (objlen != 1) { sc_debug(ctx, SC_LOG_DEBUG_ASN1, "invalid ASN.1 object length: %"SC_FORMAT_LEN_SIZE_T"u\n", objlen); r = SC_ERROR_INVALID_ASN1_OBJECT; } else *((int *) parm) = obj[0] ? 1 : 0; } break; case SC_ASN1_INTEGER: case SC_ASN1_ENUMERATED: if (parm != NULL) { r = sc_asn1_decode_integer(obj, objlen, (int *) entry->parm, 0); 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 (objlen < 1) { r = SC_ERROR_INVALID_ASN1_OBJECT; break; } if (entry->flags & SC_ASN1_ALLOC) { u8 **buf = (u8 **) parm; 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, 0); if (r >= 0) { *len = r; r = 0; } } break; case SC_ASN1_BIT_FIELD: if (parm != NULL) 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); /* Strip off padding zero */ if ((entry->flags & SC_ASN1_UNSIGNED) && objlen > 1 && obj[0] == 0x00) { objlen--; obj++; } /* Allocate buffer if needed */ if (entry->flags & SC_ASN1_ALLOC) { u8 **buf = (u8 **) parm; if (objlen > 0) { *buf = malloc(objlen); if (*buf == NULL) { r = SC_ERROR_OUT_OF_MEMORY; break; } } c = *len = objlen; parm = *buf; } else c = objlen > *len ? *len : objlen; memcpy(parm, obj, c); *len = c; } break; case SC_ASN1_GENERALIZEDTIME: if (parm != NULL) { size_t c; assert(len != NULL); if (entry->flags & SC_ASN1_ALLOC) { u8 **buf = (u8 **) parm; if (objlen > 0) { *buf = malloc(objlen); if (*buf == NULL) { r = SC_ERROR_OUT_OF_MEMORY; break; } } c = *len = objlen; parm = *buf; } else c = objlen > *len ? *len : objlen; memcpy(parm, obj, c); *len = c; } break; case SC_ASN1_OBJECT: if (parm != NULL) r = sc_asn1_decode_object_id(obj, objlen, (struct sc_object_id *) parm); break; case SC_ASN1_PRINTABLESTRING: case SC_ASN1_UTF8STRING: if (parm != NULL) { assert(len != NULL); if (entry->flags & SC_ASN1_ALLOC) { u8 **buf = (u8 **) parm; *buf = malloc(objlen+1); if (*buf == NULL) { r = SC_ERROR_OUT_OF_MEMORY; break; } *len = objlen+1; parm = *buf; } r = sc_asn1_decode_utf8string(obj, objlen, (u8 *) parm, len); if (entry->flags & SC_ASN1_ALLOC) { *len -= 1; } } break; case SC_ASN1_PATH: if (entry->parm != NULL) r = asn1_decode_path(ctx, obj, objlen, (sc_path_t *) parm, depth); break; case SC_ASN1_PKCS15_ID: if (entry->parm != NULL) { struct sc_pkcs15_id *id = (struct sc_pkcs15_id *) parm; size_t c = objlen > sizeof(id->value) ? sizeof(id->value) : objlen; memcpy(id->value, obj, c); id->len = c; } break; case SC_ASN1_PKCS15_OBJECT: if (entry->parm != NULL) r = asn1_decode_p15_object(ctx, obj, objlen, (struct sc_asn1_pkcs15_object *) parm, depth); break; case SC_ASN1_ALGORITHM_ID: if (entry->parm != NULL) r = sc_asn1_decode_algorithm_id(ctx, obj, objlen, (struct sc_algorithm_id *) parm, depth); break; case SC_ASN1_SE_INFO: if (entry->parm != NULL) r = asn1_decode_se_info(ctx, obj, objlen, (sc_pkcs15_sec_env_info_t ***)entry->parm, len, depth); break; case SC_ASN1_CALLBACK: if (entry->parm != NULL) r = callback_func(ctx, entry->arg, obj, objlen, depth); break; default: sc_debug(ctx, SC_LOG_DEBUG_ASN1, "invalid ASN.1 type: %d\n", entry->type); return SC_ERROR_INVALID_ASN1_OBJECT; } if (r) { sc_debug(ctx, SC_LOG_DEBUG_ASN1, "decoding of ASN.1 object '%s' failed: %s\n", entry->name, sc_strerror(r)); return r; } entry->flags |= SC_ASN1_PRESENT; 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) { int r, idx = 0; const u8 *p = in, *obj; struct sc_asn1_entry *entry = asn1; size_t left = len, objlen; sc_debug(ctx, SC_LOG_DEBUG_ASN1, "%*.*s""called, left=%"SC_FORMAT_LEN_SIZE_T"u, depth %d%s\n", depth, depth, "", left, depth, choice ? ", choice" : ""); if (!p) return SC_ERROR_ASN1_OBJECT_NOT_FOUND; if (left < 2) { while (asn1->name && (asn1->flags & SC_ASN1_OPTIONAL)) asn1++; /* If all elements were optional, there's nothing * to complain about */ if (asn1->name == NULL) return 0; sc_debug(ctx, SC_LOG_DEBUG_ASN1, "End of ASN.1 stream, " "non-optional field \"%s\" not found\n", asn1->name); return SC_ERROR_ASN1_OBJECT_NOT_FOUND; } if (p[0] == 0 || p[0] == 0xFF || len == 0) return SC_ERROR_ASN1_END_OF_CONTENTS; for (idx = 0; asn1[idx].name != NULL; idx++) { entry = &asn1[idx]; sc_debug(ctx, SC_LOG_DEBUG_ASN1, "Looking for '%s', tag 0x%x%s%s\n", entry->name, entry->tag, choice? ", CHOICE" : "", (entry->flags & SC_ASN1_OPTIONAL)? ", OPTIONAL": ""); /* Special case CHOICE has no tag */ if (entry->type == SC_ASN1_CHOICE) { r = asn1_decode(ctx, (struct sc_asn1_entry *) entry->parm, p, left, &p, &left, 1, depth + 1); if (r >= 0) r = 0; goto decode_ok; } obj = sc_asn1_skip_tag(ctx, &p, &left, entry->tag, &objlen); if (obj == NULL) { sc_debug(ctx, SC_LOG_DEBUG_ASN1, "'%s' not present\n", entry->name); if (choice) continue; if (entry->flags & SC_ASN1_OPTIONAL) continue; sc_debug(ctx, SC_LOG_DEBUG_ASN1, "mandatory ASN.1 object '%s' not found\n", entry->name); if (left) { u8 line[128], *linep = line; size_t i; line[0] = 0; for (i = 0; i < 10 && i < left; i++) { sprintf((char *) linep, "%02X ", p[i]); linep += 3; } 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); decode_ok: if (r) return r; if (choice) break; } if (choice && asn1[idx].name == NULL) /* No match */ SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_ASN1, SC_ERROR_ASN1_OBJECT_NOT_FOUND); if (newp != NULL) *newp = p; if (len_left != NULL) *len_left = left; if (choice) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_ASN1, idx); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_ASN1, 0); } int sc_asn1_decode(sc_context_t *ctx, struct sc_asn1_entry *asn1, const u8 *in, size_t len, const u8 **newp, size_t *len_left) { return asn1_decode(ctx, asn1, in, len, newp, len_left, 0, 0); } int sc_asn1_decode_choice(sc_context_t *ctx, struct sc_asn1_entry *asn1, const u8 *in, size_t len, const u8 **newp, size_t *len_left) { return asn1_decode(ctx, asn1, in, len, newp, len_left, 1, 0); } static int asn1_encode_entry(sc_context_t *ctx, const struct sc_asn1_entry *entry, u8 **obj, size_t *objlen, int depth) { void *parm = entry->parm; int (*callback_func)(sc_context_t *nctx, void *arg, u8 **nobj, size_t *nobjlen, int ndepth); const size_t *len = (const size_t *) entry->arg; int r = 0; u8 * buf = NULL; size_t buflen = 0; callback_func = parm; sc_debug(ctx, SC_LOG_DEBUG_ASN1, "%*.*sencoding '%s'%s\n", depth, depth, "", entry->name, (entry->flags & SC_ASN1_PRESENT)? "" : " (not present)"); if (!(entry->flags & SC_ASN1_PRESENT)) goto no_object; sc_debug(ctx, SC_LOG_DEBUG_ASN1, "%*.*stype=%d, tag=0x%02x, parm=%p, len=%"SC_FORMAT_LEN_SIZE_T"u\n", depth, depth, "", entry->type, entry->tag, parm, len ? *len : 0); if (entry->type == SC_ASN1_CHOICE) { const struct sc_asn1_entry *list, *choice = NULL; list = (const struct sc_asn1_entry *) parm; while (list->name != NULL) { if (list->flags & SC_ASN1_PRESENT) { if (choice) { sc_debug(ctx, SC_LOG_DEBUG_ASN1, "ASN.1 problem: more than " "one CHOICE when encoding %s: " "%s and %s both present\n", entry->name, choice->name, list->name); return SC_ERROR_INVALID_ASN1_OBJECT; } choice = list; } list++; } if (choice == NULL) goto no_object; return asn1_encode_entry(ctx, choice, obj, objlen, depth + 1); } if (entry->type != SC_ASN1_NULL && parm == NULL) { sc_debug(ctx, SC_LOG_DEBUG_ASN1, "unexpected parm == NULL\n"); return SC_ERROR_INVALID_ASN1_OBJECT; } switch (entry->type) { case SC_ASN1_STRUCT: r = asn1_encode(ctx, (const struct sc_asn1_entry *) parm, &buf, &buflen, depth + 1); break; case SC_ASN1_NULL: buf = NULL; buflen = 0; break; case SC_ASN1_BOOLEAN: buf = malloc(1); if (buf == NULL) { r = SC_ERROR_OUT_OF_MEMORY; break; } buf[0] = *((int *) parm) ? 0xFF : 0; buflen = 1; break; case SC_ASN1_INTEGER: case SC_ASN1_ENUMERATED: r = asn1_encode_integer(*((int *) entry->parm), &buf, &buflen); break; case SC_ASN1_BIT_STRING_NI: case SC_ASN1_BIT_STRING: 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: 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: 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; } break; case SC_ASN1_GENERALIZEDTIME: 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; } break; case SC_ASN1_OBJECT: r = sc_asn1_encode_object_id(&buf, &buflen, (struct sc_object_id *) parm); break; case SC_ASN1_PATH: r = asn1_encode_path(ctx, (const sc_path_t *) parm, &buf, &buflen, depth, entry->flags); break; case SC_ASN1_PKCS15_ID: { const struct sc_pkcs15_id *id = (const struct sc_pkcs15_id *) parm; buf = malloc(id->len); if (buf == NULL) { r = SC_ERROR_OUT_OF_MEMORY; break; } memcpy(buf, id->value, id->len); buflen = id->len; } break; case SC_ASN1_PKCS15_OBJECT: r = asn1_encode_p15_object(ctx, (const struct sc_asn1_pkcs15_object *) parm, &buf, &buflen, depth); break; case SC_ASN1_ALGORITHM_ID: r = sc_asn1_encode_algorithm_id(ctx, &buf, &buflen, (const struct sc_algorithm_id *) parm, depth); break; case SC_ASN1_SE_INFO: if (!len) return SC_ERROR_INVALID_ASN1_OBJECT; r = asn1_encode_se_info(ctx, (struct sc_pkcs15_sec_env_info **)parm, *len, &buf, &buflen, depth); break; case SC_ASN1_CALLBACK: r = callback_func(ctx, entry->arg, &buf, &buflen, depth); break; default: sc_debug(ctx, SC_LOG_DEBUG_ASN1, "invalid ASN.1 type: %d\n", entry->type); return SC_ERROR_INVALID_ASN1_OBJECT; } if (r) { sc_debug(ctx, SC_LOG_DEBUG_ASN1, "encoding of ASN.1 object '%s' failed: %s\n", entry->name, sc_strerror(r)); if (buf) free(buf); return r; } /* Treatment of OPTIONAL elements: * - if the encoding has 0 length, and the element is OPTIONAL, * we don't write anything (unless it's an ASN1 NULL and the * SC_ASN1_PRESENT flag is set). * - if the encoding has 0 length, but the element is non-OPTIONAL, * constructed, we write a empty element (e.g. a SEQUENCE of * length 0). In case of an ASN1 NULL just write the tag and * length (i.e. 0x05,0x00). * - any other empty objects are considered bogus */ no_object: if (!buflen && entry->flags & SC_ASN1_OPTIONAL && !(entry->flags & SC_ASN1_PRESENT)) { /* This happens when we try to encode e.g. the * subClassAttributes, which may be empty */ *obj = NULL; *objlen = 0; r = 0; } else if (!buflen && (entry->flags & SC_ASN1_EMPTY_ALLOWED)) { *obj = NULL; *objlen = 0; r = asn1_write_element(ctx, entry->tag, buf, buflen, obj, objlen); if (r) sc_debug(ctx, SC_LOG_DEBUG_ASN1, "error writing ASN.1 tag and length: %s\n", sc_strerror(r)); } else if (buflen || entry->type == SC_ASN1_NULL || entry->tag & SC_ASN1_CONS) { r = asn1_write_element(ctx, entry->tag, buf, buflen, obj, objlen); if (r) sc_debug(ctx, SC_LOG_DEBUG_ASN1, "error writing ASN.1 tag and length: %s\n", sc_strerror(r)); } else if (!(entry->flags & SC_ASN1_PRESENT)) { sc_debug(ctx, SC_LOG_DEBUG_ASN1, "cannot encode non-optional ASN.1 object: not given by caller\n"); r = SC_ERROR_INVALID_ASN1_OBJECT; } else { sc_debug(ctx, SC_LOG_DEBUG_ASN1, "cannot encode empty non-optional ASN.1 object\n"); r = SC_ERROR_INVALID_ASN1_OBJECT; } if (buf) free(buf); if (r >= 0) sc_debug(ctx, SC_LOG_DEBUG_ASN1, "%*.*slength of encoded item=%"SC_FORMAT_LEN_SIZE_T"u\n", depth, depth, "", *objlen); return r; } static int asn1_encode(sc_context_t *ctx, const struct sc_asn1_entry *asn1, u8 **ptr, size_t *size, int depth) { int r, idx = 0; 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) { if (obj) free(obj); if (buf) free(buf); return r; } /* in case of an empty (optional) element continue with * the next asn1 element */ if (!objsize) continue; tmp = (u8 *) realloc(buf, total + objsize); if (!tmp) { if (obj) free(obj); if (buf) free(buf); return SC_ERROR_OUT_OF_MEMORY; } buf = tmp; memcpy(buf + total, obj, objsize); free(obj); obj = NULL; total += objsize; } *ptr = buf; *size = total; return 0; } int sc_asn1_encode(sc_context_t *ctx, const struct sc_asn1_entry *asn1, u8 **ptr, size_t *size) { return asn1_encode(ctx, asn1, ptr, size, 0); } int _sc_asn1_encode(sc_context_t *ctx, const struct sc_asn1_entry *asn1, u8 **ptr, size_t *size, int depth) { return asn1_encode(ctx, asn1, ptr, size, depth); } int _sc_asn1_decode(sc_context_t *ctx, struct sc_asn1_entry *asn1, const u8 *in, size_t len, const u8 **newp, size_t *left, int choice, int depth) { return asn1_decode(ctx, asn1, in, len, newp, left, choice, depth); } int sc_der_copy(sc_pkcs15_der_t *dst, const sc_pkcs15_der_t *src) { 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; dst->len = src->len; memcpy(dst->value, src->value, src->len); } return SC_SUCCESS; } int sc_encode_oid (struct sc_context *ctx, struct sc_object_id *id, unsigned char **out, size_t *size) { static const struct sc_asn1_entry c_asn1_object_id[2] = { { "oid", SC_ASN1_OBJECT, SC_ASN1_TAG_OBJECT, SC_ASN1_ALLOC, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; struct sc_asn1_entry asn1_object_id[2]; int rv; sc_copy_asn1_entry(c_asn1_object_id, asn1_object_id); sc_format_asn1_entry(asn1_object_id + 0, id, NULL, 1); rv = _sc_asn1_encode(ctx, asn1_object_id, out, size, 1); LOG_TEST_RET(ctx, rv, "Cannot encode object ID"); return SC_SUCCESS; } #define C_ASN1_SIG_VALUE_SIZE 2 static struct sc_asn1_entry c_asn1_sig_value[C_ASN1_SIG_VALUE_SIZE] = { { "ECDSA-Sig-Value", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; #define C_ASN1_SIG_VALUE_COEFFICIENTS_SIZE 3 static struct sc_asn1_entry c_asn1_sig_value_coefficients[C_ASN1_SIG_VALUE_COEFFICIENTS_SIZE] = { { "r", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_INTEGER, SC_ASN1_ALLOC|SC_ASN1_UNSIGNED, NULL, NULL }, { "s", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_INTEGER, SC_ASN1_ALLOC|SC_ASN1_UNSIGNED, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; int sc_asn1_sig_value_rs_to_sequence(struct sc_context *ctx, 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 = in, *s = in + inlen/2; size_t r_len = inlen/2, s_len = inlen/2; int rv; LOG_FUNC_CALLED(ctx); /* R/S are filled up with zeroes, we do not want that in sequence format */ while(r_len > 1 && *r == 0x00) { r++; r_len--; } while(s_len > 1 && *s == 0x00) { s++; s_len--; } sc_copy_asn1_entry(c_asn1_sig_value, asn1_sig_value); sc_format_asn1_entry(asn1_sig_value + 0, asn1_sig_value_coefficients, NULL, 1); sc_copy_asn1_entry(c_asn1_sig_value_coefficients, asn1_sig_value_coefficients); sc_format_asn1_entry(asn1_sig_value_coefficients + 0, r, &r_len, 1); sc_format_asn1_entry(asn1_sig_value_coefficients + 1, s, &s_len, 1); rv = sc_asn1_encode(ctx, asn1_sig_value, buf, buflen); LOG_TEST_RET(ctx, rv, "ASN.1 encoding ECDSA-SIg-Value failed"); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } int 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 = NULL, *s = NULL; size_t r_len = 0, s_len = 0, halflen = buflen/2; int rv; LOG_FUNC_CALLED(ctx); if (!buf || !buflen) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); sc_copy_asn1_entry(c_asn1_sig_value, asn1_sig_value); sc_format_asn1_entry(asn1_sig_value + 0, asn1_sig_value_coefficients, NULL, 0); sc_copy_asn1_entry(c_asn1_sig_value_coefficients, asn1_sig_value_coefficients); sc_format_asn1_entry(asn1_sig_value_coefficients + 0, &r, &r_len, 0); 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_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 err; } memset(buf, 0, buflen); 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)); sc_log(ctx, "s(%"SC_FORMAT_LEN_SIZE_T"u): %s", halflen, sc_dump_hex(buf + halflen, halflen)); rv = SC_SUCCESS; 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); } OpenSC-0.26.1/src/libopensc/asn1.h000066400000000000000000000176311474147347300165350ustar00rootroot00000000000000/* * asn1.h: ASN.1 header file * * Copyright (C) 2001, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _OPENSC_ASN1_H #define _OPENSC_ASN1_H #ifdef __cplusplus extern "C" { #endif #include "libopensc/opensc.h" #include "libopensc/pkcs15.h" struct sc_asn1_entry { const char *name; unsigned int type; unsigned int tag; unsigned int flags; void *parm; void *arg; }; struct sc_asn1_pkcs15_object { struct sc_pkcs15_object *p15_obj; struct sc_asn1_entry *asn1_class_attr; struct sc_asn1_entry *asn1_subclass_attr; struct sc_asn1_entry *asn1_type_attr; }; struct sc_asn1_pkcs15_algorithm_info { int id; struct sc_object_id oid; int (*decode)(struct sc_context *, void **, const u8 *, size_t, int); int (*encode)(struct sc_context *, void *, u8 **, size_t *, int); void (*free)(void *); }; /* Utility functions */ void sc_format_asn1_entry(struct sc_asn1_entry *entry, void *parm, void *arg, int set_present); void sc_copy_asn1_entry(const struct sc_asn1_entry *src, struct sc_asn1_entry *dest); /* DER tag and length parsing */ int sc_asn1_decode(struct sc_context *ctx, struct sc_asn1_entry *asn1, const u8 *in, size_t len, const u8 **newp, size_t *left); int sc_asn1_decode_choice(struct sc_context *ctx, struct sc_asn1_entry *asn1, const u8 *in, size_t len, const u8 **newp, size_t *left); int sc_asn1_encode(struct sc_context *ctx, const struct sc_asn1_entry *asn1, u8 **buf, size_t *bufsize); int _sc_asn1_decode(struct sc_context *, struct sc_asn1_entry *, const u8 *, size_t, const u8 **, size_t *, int, int); int _sc_asn1_encode(struct sc_context *, const struct sc_asn1_entry *, u8 **, size_t *, int); int sc_asn1_read_tag(const u8 ** buf, size_t buflen, unsigned int *cla_out, unsigned int *tag_out, size_t *taglen); const u8 *sc_asn1_find_tag(struct sc_context *ctx, const u8 * buf, size_t buflen, unsigned int tag, size_t *taglen); const u8 *sc_asn1_verify_tag(struct sc_context *ctx, const u8 * buf, size_t buflen, unsigned int tag, size_t *taglen); const u8 *sc_asn1_skip_tag(struct sc_context *ctx, const u8 ** buf, size_t *buflen, unsigned int tag, size_t *taglen); /* DER encoding */ /* Argument 'ptr' is set to the location of the next possible ASN.1 object. * If NULL, no action on 'ptr' is performed. * If out is NULL or outlen is zero, the length that would be written is returned. * If data is NULL, the data field will not be written. This is helpful for constructed structures. */ int sc_asn1_put_tag(unsigned int tag, const u8 * data, size_t datalen, u8 * out, size_t outlen, u8 ** ptr); /* ASN.1 printing functions */ void sc_asn1_print_tags(const u8 * buf, size_t buflen); /* ASN.1 object decoding functions */ 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, const int strict); /* non-inverting version */ int sc_asn1_decode_bit_string_ni(const u8 * inbuf, size_t inlen, 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, const struct sc_object_id *id); /* algorithm encoding/decoding */ int sc_asn1_decode_algorithm_id(struct sc_context *, const u8 *, size_t, struct sc_algorithm_id *, int); int sc_asn1_encode_algorithm_id(struct sc_context *, u8 **, size_t *, const struct sc_algorithm_id *, int); void sc_asn1_clear_algorithm_id(struct sc_algorithm_id *); /* ASN.1 object encoding functions */ int sc_asn1_write_element(sc_context_t *ctx, unsigned int tag, const u8 * data, size_t datalen, u8 ** out, size_t * outlen); 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, const unsigned char *in, size_t inlen, unsigned char *buf, size_t buflen); /* 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 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 #define SC_ASN1_PRESENT 0x00000001 #define SC_ASN1_OPTIONAL 0x00000002 #define SC_ASN1_ALLOC 0x00000004 #define SC_ASN1_UNSIGNED 0x00000008 #define SC_ASN1_EMPTY_ALLOWED 0x00000010 #define SC_ASN1_BOOLEAN 1 #define SC_ASN1_INTEGER 2 #define SC_ASN1_BIT_STRING 3 #define SC_ASN1_BIT_STRING_NI 128 #define SC_ASN1_OCTET_STRING 4 #define SC_ASN1_NULL 5 #define SC_ASN1_OBJECT 6 #define SC_ASN1_ENUMERATED 10 #define SC_ASN1_UTF8STRING 12 #define SC_ASN1_SEQUENCE 16 #define SC_ASN1_SET 17 #define SC_ASN1_PRINTABLESTRING 19 #define SC_ASN1_UTCTIME 23 #define SC_ASN1_GENERALIZEDTIME 24 /* internal structures */ #define SC_ASN1_STRUCT 129 #define SC_ASN1_CHOICE 130 #define SC_ASN1_BIT_FIELD 131 /* bit string as integer */ /* 'complex' structures */ #define SC_ASN1_PATH 256 #define SC_ASN1_PKCS15_ID 257 #define SC_ASN1_PKCS15_OBJECT 258 #define SC_ASN1_ALGORITHM_ID 259 #define SC_ASN1_SE_INFO 260 /* 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 #define SC_ASN1_TAG_CONTEXT 0x80 #define SC_ASN1_TAG_PRIVATE 0xC0 #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 #define SC_ASN1_TAG_INTEGER 2 #define SC_ASN1_TAG_BIT_STRING 3 #define SC_ASN1_TAG_OCTET_STRING 4 #define SC_ASN1_TAG_NULL 5 #define SC_ASN1_TAG_OBJECT 6 #define SC_ASN1_TAG_OBJECT_DESCRIPTOR 7 #define SC_ASN1_TAG_EXTERNAL 8 #define SC_ASN1_TAG_REAL 9 #define SC_ASN1_TAG_ENUMERATED 10 #define SC_ASN1_TAG_UTF8STRING 12 #define SC_ASN1_TAG_SEQUENCE 16 #define SC_ASN1_TAG_SET 17 #define SC_ASN1_TAG_NUMERICSTRING 18 #define SC_ASN1_TAG_PRINTABLESTRING 19 #define SC_ASN1_TAG_T61STRING 20 #define SC_ASN1_TAG_TELETEXSTRING 20 #define SC_ASN1_TAG_VIDEOTEXSTRING 21 #define SC_ASN1_TAG_IA5STRING 22 #define SC_ASN1_TAG_UTCTIME 23 #define SC_ASN1_TAG_GENERALIZEDTIME 24 #define SC_ASN1_TAG_GRAPHICSTRING 25 #define SC_ASN1_TAG_ISO64STRING 26 #define SC_ASN1_TAG_VISIBLESTRING 26 #define SC_ASN1_TAG_GENERALSTRING 27 #define SC_ASN1_TAG_UNIVERSALSTRING 28 #define SC_ASN1_TAG_BMPSTRING 30 #define SC_ASN1_TAG_ESCAPE_MARKER 31 #ifdef __cplusplus } #endif #endif OpenSC-0.26.1/src/libopensc/authentic.h000066400000000000000000000117461474147347300176600ustar00rootroot00000000000000/* * authentic.h: Specific definitions for the Oberthur's card * 'COSMO v7' with applet 'AuthentIC v3' * * Copyright (C) 2010 Viktor Tarasov * OpenTrust * * 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 _OPENSC_AUTHENTIC_V3_H #define _OPENSC_AUTHENTIC_V3_H #include "libopensc/errors.h" #include "libopensc/types.h" #include "libopensc/iso7816.h" #ifndef CKM_RSA_PKCS #define CKM_RSA_PKCS 0x00000001 #define CKM_SHA1_RSA_PKCS 0x00000006 #define CKM_SHA256_RSA_PKCS 0x00000040 #define CKM_SHA_1 0x00000220 #define CKM_SHA256 0x00000250 #endif #define AUTHENTIC_V3_CREDENTIAL_ID_MASK 7 #define AUTHENTIC_V3_CRYPTO_OBJECT_REF_MIN 0x81 #define AUTHENTIC_V3_CRYPTO_OBJECT_REF_MAX 0xFF #define _MAKE_AUTHENTIC_MAGIC(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | ((d))) #define AUTHENTIC_SDO_MAGIC _MAKE_AUTHENTIC_MAGIC('A', 'W', 'S', 'D') #define AUTHENTIC_SDO_MAGIC_UPDATE _MAKE_AUTHENTIC_MAGIC('A', 'W', 'U', 'D') #define AUTHENTIC_SDO_MAGIC_UPDATE_RSA _MAKE_AUTHENTIC_MAGIC('A', 'W', 'U', 'R') #define AUTHENTIC_OBJECT_REF_FLAG_LOCAL 0x80 #define AUTHENTIC_MECH_CREDENTIAL_PIN 0x00 #define AUTHENTIC_MECH_CREDENTIAL_BIO 0x01 #define AUTHENTIC_MECH_CREDENTIAL_DES 0x02 #define AUTHENTIC_MECH_CREDENTIAL_2DES 0x03 #define AUTHENTIC_MECH_CREDENTIAL_3DES 0x04 #define AUTHENTIC_MECH_CREDENTIAL_AES128 0x05 #define AUTHENTIC_MECH_CREDENTIAL_AES192 0x06 #define AUTHENTIC_MECH_CREDENTIAL_AES256 0x07 #define AUTHENTIC_MECH_CRYPTO_DES 0x02 #define AUTHENTIC_MECH_CRYPTO_2DES 0x03 #define AUTHENTIC_MECH_CRYPTO_3DES 0x04 #define AUTHENTIC_MECH_CRYPTO_AES128 0x05 #define AUTHENTIC_MECH_CRYPTO_AES192 0x06 #define AUTHENTIC_MECH_CRYPTO_AES256 0x07 #define AUTHENTIC_MECH_CRYPTO_RSA1024 0x08 #define AUTHENTIC_MECH_CRYPTO_RSA1280 0x09 #define AUTHENTIC_MECH_CRYPTO_RSA1536 0x0A #define AUTHENTIC_MECH_CRYPTO_RSA1792 0x0B #define AUTHENTIC_MECH_CRYPTO_RSA2048 0x0C #define AUTHENTIC_TAG_DOCP 0xA1 #define AUTHENTIC_TAG_DOCP_MECH 0x80 #define AUTHENTIC_TAG_DOCP_ID 0x83 #define AUTHENTIC_TAG_DOCP_ACLS 0x86 #define AUTHENTIC_TAG_DOCP_SCP 0x87 #define AUTHENTIC_TAG_DOCP_USAGE_COUNTER 0x90 #define AUTHENTIC_TAG_RSA 0xA5 #define AUTHENTIC_TAG_RSA_PRIVATE 0x7F48 #define AUTHENTIC_TAG_RSA_PRIVATE_P 0x92 #define AUTHENTIC_TAG_RSA_PRIVATE_Q 0x93 #define AUTHENTIC_TAG_RSA_PRIVATE_PQ 0x94 #define AUTHENTIC_TAG_RSA_PRIVATE_DP1 0x95 #define AUTHENTIC_TAG_RSA_PRIVATE_DQ1 0x96 #define AUTHENTIC_TAG_RSA_PUBLIC 0x7F49 #define AUTHENTIC_TAG_RSA_PUBLIC_MODULUS 0x81 #define AUTHENTIC_TAG_RSA_PUBLIC_EXPONENT 0x82 #define AUTHENTIC_TAG_RSA_GENERATE_DATA 0xAC #define AUTHENTIC_TAG_CREDENTIAL 0x5F00 #define AUTHENTIC_TAG_CREDENTIAL_TRYLIMIT 0x91 #define AUTHENTIC_TAG_CREDENTIAL_PINPOLICY 0xA1 #define AUTHENTIC_TAG_CREDENTIAL_PINPOLICY_MAXLENGTH 0x83 #define AUTHENTIC_TAG_CREDENTIAL_PINPOLICY_MINLENGTH 0x84 #define AUTHENTIC_TAG_CREDENTIAL_PINPOLICY_COMPLEXITY 0x85 #define AUTHENTIC_ALGORITHM_RSA_PKCS1 0x11 #define AUTHENTIC_ALGORITHM_RSA_X509 0x12 #define AUTHENTIC_ALGORITHM_RSA_OAEP 0x13 #define AUTHENTIC_ALGORITHM_RSA_ISO9796 0x14 #define AUTHENTIC_TAG_CRT_AT 0xA4 #define AUTHENTIC_TAG_CRT_HT 0xAA #define AUTHENTIC_TAG_CRT_CCT 0xB4 #define AUTHENTIC_TAG_CRT_DST 0xB6 #define AUTHENTIC_TAG_CRT_CT 0xB8 #define AUTHENTIC_ACL_NUM_PIN_VERIFY 0 #define AUTHENTIC_ACL_NUM_PIN_RESET 1 #define AUTHENTIC_ACL_NUM_PIN_CHANGE 2 #define AUTHENTIC_ACL_NUM_PIN_MODIFY 3 #define AUTHENTIC_ACL_NUM_PIN_DELETE 4 /* SM related macros */ #define AUTHENTIC_AC_SM_MASK 0x60 #define AUTHENTIC_GP_SM_LEVEL_MASK 0x6000 #define AUTHENTIC_GP_SM_LEVEL_PLAIN 0x2000 #define AUTHENTIC_GP_SM_LEVEL_MAC 0x4000 #define AUTHENTIC_GP_SM_LEVEL_ENC_MAC 0x6000 /* * DOCP (Data Object Control Parameters) * Common holder for the all DOCP types. */ struct sc_authentic_sdo_docp { unsigned char mech; /* Crypto Mechanism ID */ unsigned char id; /* Data Object ID */ unsigned char security_parameter; /* Security Control Parameter */ unsigned char velocity_limit, try_limit; unsigned char acl_data[16]; /* Encoded AuthentIC ACL data */ size_t acl_data_len; unsigned char usage_counter[2]; }; struct sc_authentic_sdo { struct sc_authentic_sdo_docp docp; union { struct sc_pkcs15_prkey *prvkey; } data; struct sc_file *file; unsigned magic; }; #endif OpenSC-0.26.1/src/libopensc/aux-data.c000066400000000000000000000130741474147347300173670ustar00rootroot00000000000000/* * aux-data.c: Auxiliary data help functions * * Copyright (C) 2016 Viktor Tarasov * * 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" #include "common/compat_strlcat.h" #include #include #include #include int sc_aux_data_allocate(struct sc_context *ctx, struct sc_auxiliary_data **dst, struct sc_auxiliary_data *src) { int rv = SC_ERROR_INTERNAL; LOG_FUNC_CALLED(ctx); if (!dst) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Cannot allocate auxiliary data"); if (*dst == NULL) { *dst = calloc(1, sizeof(struct sc_auxiliary_data)); if (*dst == NULL) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot allocate aux. data"); } if ((src == NULL) || (src->type == SC_AUX_DATA_TYPE_NO_DATA)) LOG_FUNC_RETURN(ctx, SC_SUCCESS); switch(src->type) { case SC_AUX_DATA_TYPE_MD_CMAP_RECORD: **dst = *src; rv = SC_SUCCESS; break; default: sc_log(ctx, "Invalid aux-data type %X", src->type); LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Unknown aux-data type"); } LOG_FUNC_RETURN(ctx, rv); } int sc_aux_data_set_md_guid(struct sc_context *ctx, struct sc_auxiliary_data *aux_data, char *guid) { struct sc_md_cmap_record *rec; int rv = SC_ERROR_INTERNAL; LOG_FUNC_CALLED(ctx); if (!aux_data || !guid || strlen(guid) > SC_MD_MAX_CONTAINER_NAME_LEN) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Cannot set guid for MD container"); switch(aux_data->type) { case SC_AUX_DATA_TYPE_NO_DATA: memset(aux_data, 0, sizeof(*aux_data)); aux_data->type = SC_AUX_DATA_TYPE_MD_CMAP_RECORD; /* fallthrough */ case SC_AUX_DATA_TYPE_MD_CMAP_RECORD: rec = &aux_data->data.cmap_record; memcpy(rec->guid, guid, strlen(guid)); rec->guid_len = strlen(guid); sc_log(ctx, "set MD container GUID '%s'", aux_data->data.cmap_record.guid); rv = SC_SUCCESS; break; default: sc_log(ctx, "Invalid aux-data type %X", aux_data->type); LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Unknown aux-data type"); } LOG_FUNC_RETURN(ctx, rv); return rv; } int sc_aux_data_set_md_flags(struct sc_context *ctx, struct sc_auxiliary_data *aux_data, unsigned char flags) { int rv = SC_ERROR_INTERNAL; LOG_FUNC_CALLED(ctx); if (!aux_data) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Cannot set flags of MD container"); switch(aux_data->type) { case SC_AUX_DATA_TYPE_NO_DATA: memset(aux_data, 0, sizeof(*aux_data)); aux_data->type = SC_AUX_DATA_TYPE_MD_CMAP_RECORD; /* fallthrough */ case SC_AUX_DATA_TYPE_MD_CMAP_RECORD: aux_data->data.cmap_record.flags = flags; sc_log(ctx, "set MD container flags '0x%X'", flags); rv = SC_SUCCESS; break; default: sc_log(ctx, "Invalid aux-data type %X", aux_data->type); LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Unknown aux-data type"); } LOG_FUNC_RETURN(ctx, rv); } int sc_aux_data_get_md_guid(struct sc_context *ctx, struct sc_auxiliary_data *aux_data, unsigned flags, unsigned char *out, size_t *out_size) { struct sc_md_cmap_record *cmap_record = NULL; char guid[SC_MD_MAX_CONTAINER_NAME_LEN + 3]; LOG_FUNC_CALLED(ctx); if(!aux_data || !out || !out_size) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); if (aux_data->type != SC_AUX_DATA_TYPE_MD_CMAP_RECORD) LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); cmap_record = &aux_data->data.cmap_record; /* Ignore silently request of '{}' frame is output buffer is too small */ if (!flags && *out_size < strlen((char *)cmap_record->guid) + 2) flags = 1; *guid = '\0'; if (!flags) strncpy(guid, "{", sizeof guid); strlcat(guid, (char *)cmap_record->guid, sizeof(guid)-1); if (!flags) strlcat(guid, "}", sizeof(guid)); if (*out_size < strlen(guid)) { sc_log(ctx, "aux-data: buffer too small: out_size:%"SC_FORMAT_LEN_SIZE_T"u < guid-length:%"SC_FORMAT_LEN_SIZE_T"u", *out_size, strlen(guid)); LOG_FUNC_RETURN(ctx, SC_ERROR_BUFFER_TOO_SMALL); } memset(out, 0, *out_size); memcpy(out, guid, strlen(guid)); *out_size = strlen(guid); sc_log(ctx, "aux-data: returns guid '%s'", (char *)out); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } int sc_aux_data_get_md_flags(struct sc_context *ctx, struct sc_auxiliary_data *aux_data, unsigned char *flags) { LOG_FUNC_CALLED(ctx); if(!aux_data || !flags) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); if (aux_data->type != SC_AUX_DATA_TYPE_MD_CMAP_RECORD) LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); *flags = aux_data->data.cmap_record.flags; sc_log(ctx, "aux-data: returns flags '0x%X'", *flags); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } void sc_aux_data_free(struct sc_auxiliary_data **data) { if (data == NULL || *data == NULL) return; switch((*data)->type) { case SC_AUX_DATA_TYPE_MD_CMAP_RECORD: free(*data); break; default: break; } *data = NULL; } OpenSC-0.26.1/src/libopensc/aux-data.h000066400000000000000000000053741474147347300174000ustar00rootroot00000000000000/* * aux-data.h: Non PKCS#15, non ISO7816 data * 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 * * 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 _AUX_DATA_H #define _AUX_DATA_H #ifdef __cplusplus extern "C" { #endif #include "cardctl.h" #include "errors.h" #include "asn1.h" #include "types.h" #define SC_AUX_DATA_TYPE_NO_DATA 0x00 #define SC_AUX_DATA_TYPE_MD_CMAP_RECORD 0x01 /* From Windows Smart Card Minidriver Specification * Version 7.06 * * #define MAX_CONTAINER_NAME_LEN 39 * #define CONTAINER_MAP_VALID_CONTAINER 1 * #define CONTAINER_MAP_DEFAULT_CONTAINER 2 * typedef struct _CONTAINER_MAP_RECORD * { * WCHAR wszGuid [MAX_CONTAINER_NAME_LEN + 1]; * BYTE bFlags; * BYTE bReserved; * WORD wSigKeySizeBits; * WORD wKeyExchangeKeySizeBits; * } CONTAINER_MAP_RECORD, *PCONTAINER_MAP_RECORD; */ #define SC_MD_MAX_CONTAINER_NAME_LEN 39 #define SC_MD_CONTAINER_MAP_VALID_CONTAINER 0x01 #define SC_MD_CONTAINER_MAP_DEFAULT_CONTAINER 0x02 struct sc_md_cmap_record { unsigned char guid[SC_MD_MAX_CONTAINER_NAME_LEN + 1]; size_t guid_len; unsigned flags; unsigned keysize_sign; unsigned keysize_keyexchange; }; struct sc_auxiliary_data { unsigned type; union { struct sc_md_cmap_record cmap_record; } data; }; int sc_aux_data_set_md_flags(struct sc_context *, struct sc_auxiliary_data *, unsigned char); int sc_aux_data_allocate(struct sc_context *, struct sc_auxiliary_data **, struct sc_auxiliary_data *); int sc_aux_data_set_md_guid(struct sc_context *, struct sc_auxiliary_data *, char *); void sc_aux_data_free(struct sc_auxiliary_data **); int sc_aux_data_get_md_guid(struct sc_context *, struct sc_auxiliary_data *, unsigned, unsigned char *, size_t *); int sc_aux_data_get_md_flags(struct sc_context *, struct sc_auxiliary_data *, unsigned char *); #ifdef __cplusplus } #endif #endif /* ifndef _AUX_DATA_H */ OpenSC-0.26.1/src/libopensc/base64.c000066400000000000000000000101131474147347300167360ustar00rootroot00000000000000/* * base64.c: Base64 converting functions * * Copyright (C) 2001, 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., 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 const u8 base64_table[66] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" "0123456789+/="; static const u8 bin_table[128] = { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xE0,0xD0,0xFF,0xFF,0xD0,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xE0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0x3E,0xFF,0xF2,0xFF,0x3F, 0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B, 0x3C,0x3D,0xFF,0xFF,0xFF,0xC0,0xFF,0xFF, 0xFF,0x00,0x01,0x02,0x03,0x04,0x05,0x06, 0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E, 0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16, 0x17,0x18,0x19,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20, 0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28, 0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,0x30, 0x31,0x32,0x33,0xFF,0xFF,0xFF,0xFF,0xFF, }; static void to_base64(unsigned int i, u8 *out, size_t fillers) { 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++; } } 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) return 0; b = bin_table[k]; if (b == 0xC0) /* '=' */ break; switch (b) { case 0xD0: /* '\n' or '\r' */ c--; continue; } if (b > 0x3f) return -1; res |= b << s; s -= 6; } *skip = in - in0; *out = res; return c * 6 / 8; } int sc_base64_encode(const u8 *in, size_t len, u8 *out, size_t outlen, size_t linelength) { unsigned int chars = 0; unsigned int i, c; linelength -= linelength & 0x03; while (len >= 3) { i = in[2] + (in[1] << 8) + (in[0] << 16); in += 3; len -= 3; if (outlen < 4) return SC_ERROR_BUFFER_TOO_SMALL; to_base64(i, out, 0); out += 4; outlen -= 4; chars += 4; if (chars >= linelength && linelength > 0) { if (outlen < 1) return SC_ERROR_BUFFER_TOO_SMALL; *out = '\n'; out++; outlen--; chars = 0; } } i = c = 0; while (c < len) i |= ((unsigned int) *in++) << ((2 - c++) << 3); if (len) { if (outlen < 4) return SC_ERROR_BUFFER_TOO_SMALL; to_base64(i, out, 3 - len); out += 4; outlen -= 4; chars += 4; } if (chars && linelength > 0) { if (outlen < 1) return SC_ERROR_BUFFER_TOO_SMALL; *out = '\n'; out++; outlen--; } if (outlen < 1) return SC_ERROR_BUFFER_TOO_SMALL; *out = 0; return 0; } int sc_base64_decode(const char *in, u8 *out, size_t outlen) { 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; if (r < 3) finished = 1; while (r--) { if (outlen <= 0) return SC_ERROR_BUFFER_TOO_SMALL; *out++ = i >> s; s -= 8; outlen--; len++; } in += skip; if (finished || *in == 0) return len; } if (r == 0) return len; return SC_ERROR_INVALID_ARGUMENTS; }OpenSC-0.26.1/src/libopensc/card-asepcos.c000066400000000000000000000770561474147347300202410ustar00rootroot00000000000000/* * Copyright (c) 2007 Athena Smartcard Solutions Inc. * * 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 "asn1.h" #include "cardctl.h" static const struct sc_card_operations *iso_ops = NULL; struct sc_card_operations asepcos_ops; static struct sc_card_driver asepcos_drv = { "Athena ASEPCOS", "asepcos", &asepcos_ops, NULL, 0, NULL }; 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 } }; static int asepcos_match_card(sc_card_t *card) { int i = _sc_match_atr(card, asepcos_atrs, &card->type); if (i < 0) return 0; return 1; } static int asepcos_select_asepcos_applet(sc_card_t *card) { static const u8 asepcos_aid[] = {0xA0,0x00,0x00,0x01,0x64,0x41,0x53,0x45,0x50,0x43,0x4F,0x53,0x00}; sc_path_t tpath; int r; memset(&tpath, 0, sizeof(sc_path_t)); tpath.type = SC_PATH_TYPE_DF_NAME; tpath.len = sizeof(asepcos_aid); memcpy(tpath.value, asepcos_aid, sizeof(asepcos_aid)); r = sc_select_file(card, &tpath, NULL); if (r != SC_SUCCESS) { sc_log(card->ctx, "unable to select ASEPCOS applet"); return r; } return SC_SUCCESS; } static int asepcos_init(sc_card_t *card) { unsigned long flags; card->name = "Athena ASEPCOS"; card->cla = 0x00; /* in case of a Java card try to select the ASEPCOS applet */ if (card->type == SC_CARD_TYPE_ASEPCOS_JAVA) { int r = asepcos_select_asepcos_applet(card); if (r != SC_SUCCESS) return SC_ERROR_INVALID_CARD; } /* Set up algorithm info. */ flags = 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); _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->caps |= SC_CARD_CAP_APDU_EXT | SC_CARD_CAP_USE_FCI_AC; return SC_SUCCESS; } /* tables to map the asepcos access mode bytes to the OpenSC * access mode flags */ typedef struct { unsigned int am; unsigned int sc; } amode_entry_t; static const amode_entry_t df_amode_table[] = { { 0x40, SC_AC_OP_DELETE_SELF }, /* DELETE self */ { 0x01, SC_AC_OP_DELETE }, /* DELETE child */ { 0x10, SC_AC_OP_INVALIDATE }, /* DEACTIVATE FILE */ { 0x08, SC_AC_OP_REHABILITATE },/* ACTIVATE FILE */ { 0x04, SC_AC_OP_CREATE }, /* CREATE DF */ { 0x02, SC_AC_OP_CREATE }, /* CREATE EF */ { 0, 0 } }; static const amode_entry_t wef_amode_table[] = { { 0x04, SC_AC_OP_WRITE }, { 0x02, SC_AC_OP_UPDATE }, { 0x01, SC_AC_OP_READ }, { 0, 0 }, }; static const amode_entry_t ief_amode_table[] = { { 0x90, SC_AC_OP_REHABILITATE }, /* UPDATE is also used when a new key is generated */ { 0x82, SC_AC_OP_UPDATE }, { 0, 0 }, }; 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 transport PIN * and is presented as 'AUT' key with reference '0'*/ if (meth == SC_AC_CHV && ac == 0) meth = SC_AC_AUT; if (file->type == SC_FILE_TYPE_DF) table = df_amode_table; else if (file->type == SC_FILE_TYPE_WORKING_EF) table = wef_amode_table; else if (file->type == SC_FILE_TYPE_INTERNAL_EF) table = ief_amode_table; else return SC_ERROR_INVALID_ARGUMENTS; for (; table->am != 0; table++) { if (table->am & am) sc_file_add_acl_entry(file, table->sc, meth, ac); } return SC_SUCCESS; } /* Convert asepcos security attributes to opensc access conditions. */ static int asepcos_parse_sec_attr(sc_card_t *card, sc_file_t *file, const u8 *buf, size_t len) { const u8 *p = buf; while (len > 0) { unsigned int amode, tlen = 3; 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) 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) return r; tlen += 2; } 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 >= 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_log(card->ctx, "invalid security condition"); return SC_ERROR_INTERNAL; } p += tlen; len -= tlen; } return SC_SUCCESS; } /* sets a TLV encoded path as returned from GET DATA in a sc_path_t object */ static int asepcos_tlvpath_to_scpath(sc_path_t *out, const u8 *in, size_t in_len) { int r; size_t len = in_len; memset(out, 0, sizeof(sc_path_t)); while (len != 0) { if (len < 4) return SC_ERROR_INTERNAL; if (in[0] != 0x8b || in[1] != 0x02) return SC_ERROR_INVALID_ASN1_OBJECT; /* append file id to the path */ r = sc_append_path_id(out, &in[2], 2); if (r != SC_SUCCESS) return r; len -= 4; in += 4; } out->type = SC_PATH_TYPE_PATH; return SC_SUCCESS; } /* returns the currently selected DF (if a EF is currently selected * it returns the path from the MF to the DF in which the EF is * located. * @param card sc_card_t object to use * @param path OUT path from the MF to the current DF * @return SC_SUCCESS on success and an error value otherwise */ static int asepcos_get_current_df_path(sc_card_t *card, sc_path_t *path) { 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, 0x83); 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_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 * asepcos specific security attributes. */ static int asepcos_select_file(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file) { int r; sc_path_t npath = *in_path; 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 * and application names in it let's ignore the current * DF if the returned path contains a unsupported tag. */ if (r != SC_ERROR_INVALID_ASN1_OBJECT && r != SC_SUCCESS) return r; if (r == SC_SUCCESS && sc_compare_path_prefix(&tpath, &npath) != 0) { /* remove the currently selected DF from the path */ if (tpath.len == npath.len) { /* we are already in the requested DF */ if (file == NULL) /* no file information requested => * nothing to do */ return SC_SUCCESS; } else { /* shorten path */ r = sc_path_set(&npath, 0, &in_path->value[tpath.len], npath.len - tpath.len, 0, 0); if (r != SC_SUCCESS) return r; if (npath.len == 2) npath.type = SC_PATH_TYPE_FILE_ID; else npath.type = SC_PATH_TYPE_PATH; } } } r = iso_ops->select_file(card, &npath, file); /* XXX: this doesn't look right */ 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_log(card->ctx, "error parsing security attributes"); } LOG_FUNC_RETURN(card->ctx, r); } static int asepcos_set_security_env(sc_card_t *card, const sc_security_env_t *env, int se_num) { return SC_SUCCESS; } static int asepcos_akn_to_fileid(sc_card_t *card, sc_cardctl_asepcos_akn2fileid_t *p) { int r; u8 sbuf[32], rbuf[SC_MAX_APDU_BUFFER_SIZE]; sc_apdu_t apdu = {0}; sbuf[0] = p->akn & 0xff; sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x28, 0x02, 0x01); apdu.cla |= 0x80; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = 256; apdu.lc = 1; apdu.datalen = 1; apdu.data = sbuf; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.resplen != 4) return SC_ERROR_INTERNAL; p->fileid = (apdu.resp[1] << 16) | (apdu.resp[2] << 8) | apdu.resp[3]; return SC_SUCCESS; } /* sets the security attribute of a EF/DF */ static int asepcos_set_sec_attributes(sc_card_t *card, const u8 *data, size_t len, int is_ef) { int r, type = is_ef != 0 ? 0x02 : 0x04; sc_apdu_t apdu = {0}; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x8a, type, 0xab); apdu.cla |= 0x80; apdu.lc = len; apdu.datalen = len; apdu.data = data; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); } /* encodes the opensc file attributes into the card specific format */ static int asepcos_set_security_attributes(sc_card_t *card, sc_file_t *file) { size_t i; const amode_entry_t *table; u8 buf[64], *p; int r = SC_SUCCESS; /* 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, file->sec_attr_len, file->type == SC_FILE_TYPE_DF ? 0:1); /* otherwise construct the ACL from the opensc ACLs */ if (file->type == SC_FILE_TYPE_DF) table = df_amode_table; else if (file->type == SC_FILE_TYPE_WORKING_EF) table = wef_amode_table; else if (file->type == SC_FILE_TYPE_INTERNAL_EF) table = ief_amode_table; else return SC_ERROR_INVALID_ARGUMENTS; p = buf; for (i = 0; table[i].am != 0; i++) { const struct sc_acl_entry *ent = sc_file_get_acl_entry(file, table[i].sc); if (ent == NULL) continue; *p++ = 0x80; *p++ = 0x01; *p++ = table[i].am & 0xff; if (ent->method == SC_AC_NONE) { *p++ = 0x90; *p++ = 0x00; } else if (ent->method == SC_AC_NEVER) { *p++ = 0x97; *p++ = 0x00; } else if (ent->method == SC_AC_CHV) { sc_cardctl_asepcos_akn2fileid_t st; st.akn = ent->key_ref; r = asepcos_akn_to_fileid(card, &st); if (r != SC_SUCCESS) return r; *p++ = 0xa0; *p++ = 0x05; *p++ = 0x89; *p++ = 0x03; *p++ = (st.fileid >> 16) & 0xff; *p++ = (st.fileid >> 8 ) & 0xff; *p++ = st.fileid & 0xff; } else { sc_log(card->ctx, "unknown auth method: '%d'", ent->method); return SC_ERROR_INTERNAL; } } if (p != buf) r = asepcos_set_sec_attributes(card, buf, p-buf, file->type == SC_FILE_TYPE_DF ? 0:1); return r; } static int asepcos_decipher(sc_card_t *card, const u8 * crgram, size_t crgram_len, u8 * out, size_t outlen) { int r; sc_apdu_t apdu; 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); apdu.cla |= 0x80; apdu.resp = out; apdu.resplen = outlen; /* if less than 256 bytes are expected than set Le to 0x00 * 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); 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 (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). */ static int asepcos_compute_signature(sc_card_t *card, const u8 *data, size_t datalen, u8 *out, size_t outlen) { int r = SC_SUCCESS, atype; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; sc_apdu_t apdu; LOG_FUNC_CALLED(card->ctx); if (datalen >= 256) atype = SC_APDU_CASE_4_EXT; else atype = SC_APDU_CASE_4_SHORT; sc_format_apdu(card, &apdu, atype, 0x14, 0x01, 0x00); apdu.cla |= 0x80; apdu.lc = datalen; apdu.datalen = datalen; apdu.data = data; 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) { sc_log(card->ctx, "error creating signature"); return sc_check_sw(card, apdu.sw1, apdu.sw2); } if (apdu.resplen > outlen) return SC_ERROR_BUFFER_TOO_SMALL; memcpy(out, apdu.resp, apdu.resplen); return (int)apdu.resplen; } /* activates the EF/DF specified in the file id. */ static int asepcos_activate_file(sc_card_t *card, int fileid, int is_ef) { int r, type = is_ef != 0 ? 2 : 1; sc_apdu_t apdu; u8 sbuf[2]; sbuf[0] = (fileid >> 8) & 0xff; sbuf[1] = fileid & 0xff; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x44, type, 0x00); apdu.lc = 2; apdu.datalen = 2; apdu.data = sbuf; r = sc_transmit_apdu(card, &apdu); 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. * @param card the sc_card_t object to use * @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 = {0}; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE], *p = &sbuf[0]; *p++ = (file->id >> 8) & 0xff; *p++ = file->id & 0xff; if (file->size > 0xffff) { *p++ = (file->size >> 24) & 0xff; *p++ = (file->size >> 16) & 0xff; *p++ = (file->size >> 8 ) & 0xff; *p++ = file->size & 0xff; type = 1; } else { *p++ = (file->size >> 8) & 0xff; *p++ = file->size & 0xff; type = 0; } if (file->namelen != 0 && file->namelen <= 16) { memcpy(p, file->name, file->namelen); p += file->namelen; } sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xe0, 0x38, type); apdu.cla |= 0x80; apdu.lc = p - sbuf; apdu.datalen = p - sbuf; 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) return sc_check_sw(card, apdu.sw1, apdu.sw2); r = sc_select_file(card, &file->path, NULL); if (r != SC_SUCCESS) return r; /* set security attributes */ r = asepcos_set_security_attributes(card, file); if (r != SC_SUCCESS) { sc_log(card->ctx, "unable to set security attributes"); return r; } return SC_SUCCESS; } else if (file->type == SC_FILE_TYPE_WORKING_EF) { int r; sc_apdu_t apdu; u8 descr_byte = file->ef_structure & 7; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE], *p = &sbuf[0]; *p++ = 0x85; p++; /* file id */ *p++ = (file->id >> 8) & 0xff; *p++ = file->id & 0xff; /* record size */ if (file->ef_structure == SC_FILE_EF_TRANSPARENT) { *p++ = 0x00; *p++ = 0x00; } else { *p++ = (file->record_length >> 8) & 0xff; *p++ = file->record_length & 0xff; } /* number of records or file size */ if (file->ef_structure == SC_FILE_EF_TRANSPARENT) { *p++ = (file->size >> 8) & 0xff; *p++ = file->size & 0xff; } else { *p++ = (file->record_count >> 8) & 0xff; *p++ = file->record_count & 0xff; } /* set the length of the inner TLV object */ sbuf[1] = p - sbuf - 2; /* FIXME */ sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xe0, descr_byte, 0x00); apdu.lc = p - sbuf; apdu.datalen = p - sbuf; 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) return sc_check_sw(card, apdu.sw1, apdu.sw2); /* set security attributes */ r = asepcos_set_security_attributes(card, file); if (r != SC_SUCCESS) { 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. */ int r, atype = SC_APDU_CASE_3_SHORT; sc_apdu_t apdu; if (file->prop_attr_len > 255) atype = SC_APDU_CASE_3_EXT; sc_format_apdu(card, &apdu, atype, 0xe0, 0x08, 0x00); apdu.lc = file->prop_attr_len; apdu.datalen = file->prop_attr_len; apdu.data = file->prop_attr; r = sc_transmit_apdu(card, &apdu); 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_log(card->ctx, "unable to set security attributes"); return r; } return asepcos_activate_file(card, file->id, 1); } else return SC_ERROR_INVALID_ARGUMENTS; } /* list files: the function first calls GET DATA to get the current * working DF. It then re-selects the DF to get proprietary FCI which * contain the FID of the first child DF EF. * The FID of the other EFs/DFs within the selected DF are then * obtained by selecting the know FIDs to get next child EF/DF. * @param card the sc_card_t object to use * @param buff the output buffer for the list of FIDs * @param blen the length of the buffer * @return the number of FIDs read on success and an error value otherwise. */ static int asepcos_list_files(sc_card_t *card, u8 *buf, size_t blen) { int r, rv = 0, dfFID, efFID; sc_path_t bpath, tpath; sc_file_t *tfile = NULL; /* 1. get currently selected DF */ r = asepcos_get_current_df_path(card, &bpath); if (r != SC_SUCCESS) return r; /* 2. re-select DF to get the FID of the child EFs/DFs */ r = sc_select_file(card, &bpath, &tfile); if (r != SC_SUCCESS) return r; if (tfile->prop_attr_len != 6 || tfile->prop_attr == NULL) { sc_file_free(tfile); sc_log(card->ctx, "unable to parse proprietary FCI attributes"); return SC_ERROR_INTERNAL; } dfFID = (tfile->prop_attr[2] << 8) | tfile->prop_attr[3]; efFID = (tfile->prop_attr[4] << 8) | tfile->prop_attr[5]; sc_file_free(tfile); /* 3. select every child DF to get the FID of the next child DF */ while (dfFID != 0) { /* put DF FID on the list */ if (blen < 2) return SC_ERROR_BUFFER_TOO_SMALL; *buf++ = (dfFID >> 8) & 0xff; *buf++ = dfFID & 0xff; rv += 2; blen -= 2; /* select DF to get next DF FID */ tpath = bpath; r = sc_append_file_id(&tpath, dfFID); if (r != SC_SUCCESS) return r; r = sc_select_file(card, &tpath, &tfile); if (r != SC_SUCCESS) return r; if (tfile->prop_attr_len != 6 || tfile->prop_attr == NULL) return SC_ERROR_INTERNAL; dfFID = (tfile->prop_attr[0] << 8) | tfile->prop_attr[1]; sc_file_free(tfile); } /* 4. select every child EF ... */ while (efFID != 0) { /* put DF FID on the list */ if (blen < 2) return SC_ERROR_BUFFER_TOO_SMALL; *buf++ = (efFID >> 8) & 0xff; *buf++ = efFID & 0xff; rv += 2; blen -= 2; /* select EF to get next EF FID */ tpath = bpath; r = sc_append_file_id(&tpath, efFID); if (r != SC_SUCCESS) return r; r = sc_select_file(card, &tpath, &tfile); if (r != SC_SUCCESS) return r; if (tfile->prop_attr_len < 2 || tfile->prop_attr == NULL) return SC_ERROR_INTERNAL; efFID = (tfile->prop_attr[0] << 8) | tfile->prop_attr[1]; sc_file_free(tfile); } return rv; } static int asepcos_delete_file(sc_card_t *card, const sc_path_t *path) { int r, ftype, atype; sc_apdu_t apdu; u8 buf[SC_MAX_APDU_BUFFER_SIZE]; /* use GET DATA to determine whether it is a DF or EF */ sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0x01, 0x84); apdu.le = 256; apdu.resplen = sizeof(buf); apdu.resp = buf; r = sc_transmit_apdu(card, &apdu); 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[1] = path->value[path->len-1]; } else { /* 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); 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 * pkcs15 profile file). */ static int asepcos_get_default_key(sc_card_t *card, struct sc_cardctl_default_key *data) { static const u8 asepcos_def_key[] = {0x41,0x53,0x45,0x43,0x41,0x52,0x44,0x2b}; if (data->method != SC_AC_CHV && data->method != SC_AC_AUT) return SC_ERROR_NO_DEFAULT_KEY; if (data->key_data == NULL || data->len < sizeof(asepcos_def_key)) return SC_ERROR_BUFFER_TOO_SMALL; memcpy(data->key_data, asepcos_def_key, sizeof(asepcos_def_key)); data->len = sizeof(asepcos_def_key); return SC_SUCCESS; } static int asepcos_get_serialnr(sc_card_t *card, sc_serial_number_t *serial) { 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, 0x14); 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) { sc_log(card->ctx, "unexpected response to GET DATA serial number\n"); return SC_ERROR_INTERNAL; } /* cache serial number */ memcpy(card->serialnr.value, rbuf, 8); card->serialnr.len = 8; /* copy and return serial number */ memcpy(serial, &card->serialnr, sizeof(*serial)); return SC_SUCCESS; } static int asepcos_change_key(sc_card_t *card, sc_cardctl_asepcos_change_key_t *p) { int r, atype; sc_apdu_t apdu; if (p->datalen > 255) atype = SC_APDU_CASE_3_EXT; else atype = SC_APDU_CASE_3_SHORT; sc_format_apdu(card, &apdu, atype, 0x24, 0x01, 0x80); apdu.lc = p->datalen; apdu.datalen = p->datalen; apdu.data = p->data; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); } static int asepcos_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) { switch (cmd) { case SC_CARDCTL_GET_DEFAULT_KEY: return asepcos_get_default_key(card, (struct sc_cardctl_default_key *) ptr); case SC_CARDCTL_GET_SERIALNR: return asepcos_get_serialnr(card, (sc_serial_number_t *)ptr); case SC_CARDCTL_ASEPCOS_CHANGE_KEY: return asepcos_change_key(card, (sc_cardctl_asepcos_change_key_t*)ptr); case SC_CARDCTL_ASEPCOS_AKN2FILEID: return asepcos_akn_to_fileid(card, (sc_cardctl_asepcos_akn2fileid_t*)ptr); case SC_CARDCTL_ASEPCOS_SET_SATTR: return asepcos_set_security_attributes(card, (sc_file_t*)ptr); case SC_CARDCTL_ASEPCOS_ACTIVATE_FILE: return asepcos_activate_file(card, ((sc_cardctl_asepcos_activate_file_t*)ptr)->fileid, ((sc_cardctl_asepcos_activate_file_t *)ptr)->is_ef); } return SC_ERROR_NOT_SUPPORTED; } /* build the different APDUs for the PIN handling commands */ static int asepcos_build_pin_apdu(sc_card_t *card, sc_apdu_t *apdu, struct sc_pin_cmd_data *data, u8 *buf, size_t buf_len, unsigned int cmd, int is_puk) { int r, fileid; u8 *p = buf; sc_cardctl_asepcos_akn2fileid_t st; switch (cmd) { case SC_PIN_CMD_VERIFY: st.akn = data->pin_reference; r = asepcos_akn_to_fileid(card, &st); if (r != SC_SUCCESS) return r; fileid = st.fileid; /* the fileid of the puk is the fileid of the pin + 1 */ if (is_puk != 0) fileid++; sc_format_apdu(card, apdu, SC_APDU_CASE_3_SHORT, 0x20, 0x02, 0x80); *p++ = (fileid >> 24) & 0xff; *p++ = (fileid >> 16) & 0xff; *p++ = (fileid >> 8 ) & 0xff; *p++ = fileid & 0xff; memcpy(p, data->pin1.data, data->pin1.len); p += data->pin1.len; apdu->lc = p - buf; apdu->datalen = p - buf; apdu->data = buf; break; case SC_PIN_CMD_CHANGE: /* build the CHANGE KEY apdu. Note: the PIN file is implicitly * selected by its SFID */ *p++ = 0x81; *p++ = data->pin2.len & 0xff; memcpy(p, data->pin2.data, data->pin2.len); p += data->pin2.len; st.akn = data->pin_reference; r = asepcos_akn_to_fileid(card, &st); if (r != SC_SUCCESS) return r; fileid = 0x80 | (st.fileid & 0x1f); sc_format_apdu(card, apdu, SC_APDU_CASE_3_SHORT, 0x24, 0x01, fileid); apdu->lc = p - buf; apdu->datalen = p - buf; apdu->data = buf; break; case SC_PIN_CMD_UNBLOCK: /* 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; *p++ = data->pin2.len & 0xff; memcpy(p, data->pin2.data, data->pin2.len); p += data->pin2.len; st.akn = data->pin_reference; r = asepcos_akn_to_fileid(card, &st); if (r != SC_SUCCESS) return r; fileid = 0x80 | (st.fileid & 0x1f); sc_format_apdu(card, apdu, SC_APDU_CASE_3_SHORT, 0x2C, 0x02, fileid); apdu->lc = p - buf; apdu->datalen = p - buf; apdu->data = buf; break; default: return SC_ERROR_NOT_SUPPORTED; } return SC_SUCCESS; } /* 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) { 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_log(card->ctx, "invalid PIN1 length"); return SC_ERROR_INVALID_PIN_LENGTH; } switch (pdata->cmd) { case SC_PIN_CMD_VERIFY: if (pdata->pin_type != SC_AC_CHV && pdata->pin_type != SC_AC_AUT) return SC_ERROR_INVALID_ARGUMENTS; /* 'AUT' key is the transport PIN and should have reference '0' */ if (pdata->pin_type == SC_AC_AUT && pdata->pin_reference) return SC_ERROR_INVALID_ARGUMENTS; /* build verify APDU and send it to the card */ r = asepcos_build_pin_apdu(card, &apdu, pdata, sbuf, sizeof(sbuf), SC_PIN_CMD_VERIFY, 0); if (r != SC_SUCCESS) break; r = sc_transmit_apdu(card, &apdu); if (r != SC_SUCCESS) 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_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); if (r != SC_SUCCESS) break; r = sc_transmit_apdu(card, &apdu); if (r != SC_SUCCESS) { sc_log(card->ctx, "APDU transmit failed"); break; } if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) { /* unable to verify the old PIN */ break; } /* 2, step: use CHANGE KEY to update the PIN */ r = asepcos_build_pin_apdu(card, &apdu, pdata, sbuf, sizeof(sbuf), SC_PIN_CMD_CHANGE, 0); if (r != SC_SUCCESS) break; r = sc_transmit_apdu(card, &apdu); if (r != SC_SUCCESS) 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_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); if (r != SC_SUCCESS) break; r = sc_transmit_apdu(card, &apdu); if (r != SC_SUCCESS) { sc_log(card->ctx, "APDU transmit failed"); break; } /* 2, step: unblock and change the pin */ r = asepcos_build_pin_apdu(card, &apdu, pdata, sbuf, sizeof(sbuf), SC_PIN_CMD_UNBLOCK, 0); if (r != SC_SUCCESS) break; r = sc_transmit_apdu(card, &apdu); if (r != SC_SUCCESS) { sc_log(card->ctx, "APDU transmit failed"); break; } break; default: 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 (r == SC_SUCCESS) { if (apdu.sw1 == 0x63) { if ((apdu.sw2 & 0xF0) == 0xC0 && tries_left != NULL) *tries_left = apdu.sw2 & 0x0F; r = SC_ERROR_PIN_CODE_INCORRECT; return r; } r = 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) { if (iso_ops == NULL) iso_ops = sc_get_iso7816_driver()->ops; asepcos_ops = *iso_ops; asepcos_ops.match_card = asepcos_match_card; asepcos_ops.init = asepcos_init; asepcos_ops.select_file = asepcos_select_file; asepcos_ops.set_security_env = asepcos_set_security_env; asepcos_ops.decipher = asepcos_decipher; asepcos_ops.compute_signature = asepcos_compute_signature; asepcos_ops.create_file = asepcos_create_file; asepcos_ops.delete_file = asepcos_delete_file; 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; } struct sc_card_driver * sc_get_asepcos_driver(void) { return sc_get_driver(); } OpenSC-0.26.1/src/libopensc/card-atrust-acos.c000066400000000000000000000663521474147347300210460ustar00rootroot00000000000000/* * atrust-acos.c: Support for A-Trust ACOS based cards * * Copyright (C) 2005 Franz Brandl based on work from * Jörn Zukowski and * Nils Larsch , TrustCenter AG * * 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 "asn1.h" #include "cardctl.h" /*****************************************************************************/ #define ACOS_EMV_A03 "A-TRUST ACOS" #define ACOS_EMV_A05 "A-TRUST ACOS A05" static const char *atrust_acos_atrs[] = { "3B:BF:11:00:81:31:fe:45:45:50:41", "3B:BF:11:00:81:31:fe:45:4d:43:41", "3B:BF:13:00:81:31:fe:45:45:50:41", "3B:BF:13:00:81:31:fe:45:4d:43:41", NULL }; /* sequence and number has to match atr table ! */ static const char *atrust_acos_names[] = { ACOS_EMV_A03, ACOS_EMV_A03, ACOS_EMV_A05, ACOS_EMV_A05, NULL }; static struct sc_card_operations atrust_acos_ops; static struct sc_card_operations *iso_ops = NULL; static struct sc_card_driver atrust_acos_drv = { "A-Trust ACOS cards", "atrust-acos", &atrust_acos_ops, NULL, 0, NULL }; /* internal structure to save the current security environment */ typedef struct atrust_acos_ex_data_st { int sec_ops; /* the currently selected security operation, * i.e. SC_SEC_OPERATION_AUTHENTICATE etc. */ unsigned long fix_digestInfo; } atrust_acos_ex_data; /*****************************************************************************/ static int atrust_acos_match_card(struct sc_card *card) { int i, match = 0; 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 */ /* part of the hist chars is variable */ if (len > card->atr.len) continue; if (memcmp(card->atr.value, defatr, len) != 0) continue; match = 1; card->name = atrust_acos_names[i]; break; } return match; } /*****************************************************************************/ static int atrust_acos_init(struct sc_card *card) { unsigned int flags; atrust_acos_ex_data *ex_data; ex_data = calloc(1, sizeof(atrust_acos_ex_data)); if (ex_data == NULL) return SC_ERROR_OUT_OF_MEMORY; card->cla = 0x00; card->drv_data = (void *)ex_data; /* set the supported algorithm */ 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 (card->name != NULL && !strcmp(card->name, ACOS_EMV_A05)) flags |= SC_ALGORITHM_RSA_HASH_SHA256; _sc_card_add_rsa_alg(card, 1536, flags, 0x10001); /* we need read_binary&friends with max 128 bytes per read */ card->max_send_size = 128; card->max_recv_size = 128; return 0; } /*****************************************************************************/ static int atrust_acos_finish(struct sc_card *card) { if (card->drv_data) free((atrust_acos_ex_data *)card->drv_data); return 0; } /*****************************************************************************/ static int process_fci(struct sc_context *ctx, struct sc_file *file, const u8 *buf, size_t buflen) { size_t taglen, len = buflen; const u8 *tag = NULL, *p; sc_log(ctx, "processing FCI bytes\n"); if (buflen < 2) return SC_ERROR_INTERNAL; if (buf[0] != 0x6f) /* FCI template */ return SC_ERROR_INVALID_DATA; len = (size_t)buf[1]; if (buflen - 2 < len) return SC_ERROR_INVALID_DATA; p = buf + 2; /* defaults */ file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = SC_FILE_EF_UNKNOWN; 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_log(ctx, " bytes in file: %d\n", bytes); file->size = bytes; } /* get file type */ tag = sc_asn1_find_tag(ctx, p, len, 0x82, &taglen); if (tag != NULL) { const char *type = "unknown"; const char *structure = "unknown"; if (taglen == 1 && tag[0] == 0x01) { /* transparent EF */ type = "working EF"; structure = "transparent"; file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = SC_FILE_EF_TRANSPARENT; } else if (taglen == 1 && tag[0] == 0x11) { /* object EF */ type = "working EF"; structure = "object"; file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = SC_FILE_EF_TRANSPARENT; /* TODO */ } else if (taglen == 3 && tag[1] == 0x21) { type = "working EF"; file->record_length = tag[2]; file->type = SC_FILE_TYPE_WORKING_EF; /* linear fixed, cyclic or compute */ switch ( tag[0] ) { case 0x02: structure = "linear fixed"; file->ef_structure = SC_FILE_EF_LINEAR_FIXED; break; case 0x07: structure = "cyclic"; file->ef_structure = SC_FILE_EF_CYCLIC; break; case 0x17: structure = "compute"; file->ef_structure = SC_FILE_EF_UNKNOWN; break; default: structure = "unknown"; file->ef_structure = SC_FILE_EF_UNKNOWN; file->record_length = 0; break; } } sc_log(ctx, " type: %s\n", type); sc_log(ctx, " EF structure: %s\n", structure); } file->magic = SC_FILE_MAGIC; return SC_SUCCESS; } /*****************************************************************************/ static int atrust_acos_select_aid(struct sc_card *card, u8 aid[16], size_t len, struct sc_file **file_out) { sc_apdu_t apdu; int r; size_t i = 0; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 0x04, 0x0C); apdu.lc = len; apdu.data = (u8*)aid; apdu.datalen = len; apdu.resplen = 0; apdu.le = 0; r = sc_transmit_apdu(card, &apdu); 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); if (file_out) { sc_file_t *file = sc_file_new(); if (!file) 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++) 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); } /*****************************************************************************/ static int atrust_acos_select_fid(struct sc_card *card, unsigned int id_hi, unsigned int id_lo, struct sc_file **file_out) { sc_apdu_t apdu; u8 data[] = {id_hi & 0xff, id_lo & 0xff}; u8 resp[SC_MAX_APDU_BUFFER_SIZE]; int bIsDF = 0, r; /* request FCI to distinguish between EFs and DFs */ sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0x00, 0x00); apdu.resp = (u8*)resp; apdu.resplen = SC_MAX_APDU_BUFFER_SIZE; apdu.le = 256; apdu.lc = 2; apdu.data = (u8*)data; apdu.datalen = 2; r = sc_transmit_apdu(card, &apdu); 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()) */ bIsDF = 1; apdu.p2 = 0x0C; apdu.cse = SC_APDU_CASE_3_SHORT; apdu.resplen = 0; apdu.le = 0; r = sc_transmit_apdu(card, &apdu); 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 */ sc_apdu_t apdu2; u8 resp2[2]; sc_format_apdu(card, &apdu2, SC_APDU_CASE_2_SHORT, 0xB0, 0, 0); apdu2.resp = (u8*)resp2; apdu2.resplen = 2; apdu2.le = 1; apdu2.lc = 0; r = sc_transmit_apdu(card, &apdu2); 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; } 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) LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); file->id = (id_hi << 8) + id_lo; file->path = card->cache.current_path; if (bIsDF) { /* we have a DF */ 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; } else { /* ok, assume we have a EF */ r = process_fci(card->ctx, file, apdu.resp, apdu.resplen); if (r != SC_SUCCESS) { sc_file_free(file); return r; } *file_out = file; } } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); } /*****************************************************************************/ static int atrust_acos_select_file(struct sc_card *card, const struct sc_path *in_path, struct sc_file **file_out) { 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_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); 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 */ /* 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 */ /* 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_log(card->ctx, "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) { u8 n_pathbuf[SC_MAX_PATH_SIZE]; size_t bMatch = 0; /* Select with path (sequence of File-IDs) */ /* ACOS only supports one * level of subdirectories, therefore a path is * at most 3 FID long (the last one being the FID * of a EF) => pathlen must be even and less than 6 */ 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; } /* 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 right 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); LOG_TEST_RET(card->ctx, 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_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; ictx, r, "SELECT FILE (DF-ID) failed"); } 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); } /** atrust_acos_set_security_env * sets the security environment * \param card pointer to the sc_card object * \param env pointer to a sc_security_env object * \param se_num not used here * \return SC_SUCCESS on success or an error code * * This function sets the security environment (using the * command MANAGE SECURITY ENVIRONMENT). In case a COMPUTE SIGNATURE * operation is requested , this function tries to detect whether * COMPUTE SIGNATURE or INTERNAL AUTHENTICATE must be used for signature * calculation. */ static int atrust_acos_set_security_env(struct sc_card *card, const struct sc_security_env *env, int se_num) { u8 *p, *pp; int r, operation = env->operation; struct sc_apdu apdu; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; atrust_acos_ex_data *ex_data = (atrust_acos_ex_data *)card->drv_data; p = sbuf; /* copy key reference, if present */ if (env->flags & SC_SEC_ENV_KEY_REF_PRESENT) { if (env->flags & SC_SEC_ENV_KEY_REF_SYMMETRIC) *p++ = 0x83; else *p++ = 0x84; *p++ = env->key_ref_len; memcpy(p, env->key_ref, env->key_ref_len); p += env->key_ref_len; } pp = p; if (operation == SC_SEC_OPERATION_DECIPHER){ if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_02) { *p++ = 0x80; *p++ = 0x01; *p++ = 0x02; } else return SC_ERROR_INVALID_ARGUMENTS; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x81, 0xb8); apdu.data = sbuf; apdu.datalen = p - sbuf; apdu.lc = p - sbuf; apdu.le = 0; r = sc_transmit_apdu(card, &apdu); 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_TYPE_01 || env->algorithm_flags & SC_ALGORITHM_RSA_PAD_ISO9796)) { if (env->flags & SC_SEC_ENV_ALG_REF_PRESENT) { *p++ = 0x80; *p++ = 0x01; *p++ = env->algorithm_ref & 0xFF; } else if (env->flags & SC_SEC_ENV_ALG_PRESENT && env->algorithm == SC_ALGORITHM_RSA) { /* set the method to use based on the algorithm_flags */ *p++ = 0x80; *p++ = 0x01; 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) *p++ = 0x22; else if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_MD5) *p++ = 0x32; else { /* can't use COMPUTE SIGNATURE => * try INTERNAL AUTHENTICATE */ p = pp; operation = SC_SEC_OPERATION_AUTHENTICATE; goto try_authenticate; } } else if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_ISO9796) { if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA1) *p++ = 0x11; else if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_RIPEMD160) *p++ = 0x21; else return SC_ERROR_INVALID_ARGUMENTS; } else return SC_ERROR_INVALID_ARGUMENTS; } sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0xb6); apdu.data = sbuf; apdu.datalen = p - sbuf; apdu.lc = p - sbuf; apdu.le = 0; /* we don't know whether to use * COMPUTE SIGNATURE or INTERNAL AUTHENTICATE */ r = sc_transmit_apdu(card, &apdu); 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; return SC_SUCCESS; } /* reset pointer */ p = pp; /* doesn't work => try next op */ operation = SC_SEC_OPERATION_AUTHENTICATE; } try_authenticate: /* try INTERNAL 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); 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; ex_data->sec_ops = SC_SEC_OPERATION_AUTHENTICATE; return SC_SUCCESS; } return SC_ERROR_INVALID_ARGUMENTS; } /*****************************************************************************/ static int atrust_acos_compute_signature(struct sc_card *card, const u8 * data, size_t datalen, u8 * out, size_t outlen) { int r; struct sc_apdu apdu; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; atrust_acos_ex_data *ex_data = (atrust_acos_ex_data *)card->drv_data; if (datalen > SC_MAX_APDU_BUFFER_SIZE) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); 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); apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = 0; memcpy(sbuf, data, datalen); apdu.data = sbuf; apdu.lc = datalen; apdu.datalen = datalen; r = sc_transmit_apdu(card, &apdu); 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)); /* call COMPUTE SIGNATURE */ sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x2A, 0x9E, 0x9A); 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) { size_t len = apdu.resplen > outlen ? outlen : apdu.resplen; 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; /* call INTERNAL AUTHENTICATE */ sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x88, 0x10, 0x00); /* fix/create DigestInfo structure (if necessary) */ if (ex_data->fix_digestInfo) { unsigned int flags = ex_data->fix_digestInfo & SC_ALGORITHM_RSA_HASHES; if (flags == 0x0) /* XXX: assume no hash is wanted */ flags = SC_ALGORITHM_RSA_HASH_NONE; tmp_len = sizeof(sbuf); r = sc_pkcs1_encode(card->ctx, flags, data, datalen, sbuf, &tmp_len, sizeof(sbuf)*8, NULL); if (r < 0) return r; } else { memcpy(sbuf, data, datalen); tmp_len = datalen; } apdu.lc = tmp_len; apdu.data = sbuf; apdu.datalen = tmp_len; 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) 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, (int)len); } } else SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); /* clear old state */ ex_data->sec_ops = 0; ex_data->fix_digestInfo = 0; SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); } /*****************************************************************************/ static int atrust_acos_decipher(struct sc_card *card, const u8 * crgram, size_t crgram_len, u8 * out, size_t outlen) { int r; struct sc_apdu apdu; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; assert(card != NULL && crgram != NULL && out != NULL); LOG_FUNC_CALLED(card->ctx); if (crgram_len > 255) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); /* 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); sbuf[0] = 0; /* padding indicator byte, 0x00 = No further indication */ memcpy(sbuf + 1, crgram, crgram_len); apdu.data = sbuf; apdu.lc = crgram_len + 1; apdu.datalen = crgram_len + 1; 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) { size_t len = apdu.resplen > outlen ? outlen : apdu.resplen; memcpy(out, apdu.resp, 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)); } /*****************************************************************************/ static int atrust_acos_check_sw(struct sc_card *card, unsigned int sw1, unsigned int sw2) { 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_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); } /*****************************************************************************/ static int acos_get_serialnr(sc_card_t *card, sc_serial_number_t *serial) { int r; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; sc_apdu_t apdu; 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 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; } /*****************************************************************************/ static int atrust_acos_card_ctl(struct sc_card *card, unsigned long cmd, void *ptr) { switch (cmd) { case SC_CARDCTL_GET_SERIALNR: return acos_get_serialnr(card, (sc_serial_number_t *)ptr); default: return SC_ERROR_NOT_SUPPORTED; } } /*****************************************************************************/ static int atrust_acos_logout(struct sc_card *card) { int r; struct sc_apdu 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; r = sc_transmit_apdu(card, &apdu); 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, * that no MF exists, but then there's no need to logout * => return SC_SUCCESS */ return SC_SUCCESS; return sc_check_sw(card, apdu.sw1, apdu.sw2); } /*****************************************************************************/ 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; atrust_acos_ops.finish = atrust_acos_finish; atrust_acos_ops.select_file = atrust_acos_select_file; atrust_acos_ops.check_sw = atrust_acos_check_sw; atrust_acos_ops.create_file = NULL; atrust_acos_ops.delete_file = NULL; atrust_acos_ops.set_security_env = atrust_acos_set_security_env; atrust_acos_ops.compute_signature = atrust_acos_compute_signature; 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; } /*****************************************************************************/ struct sc_card_driver * sc_get_atrust_acos_driver(void) { return sc_get_driver(); } OpenSC-0.26.1/src/libopensc/card-authentic.c000066400000000000000000002072601474147347300205600ustar00rootroot00000000000000/* * card-authentic.c: Support for the Oberthur smart cards * with PKI applet AuthentIC v3.2 * * Copyright (C) 2010 Viktor Tarasov * OpenTrust * * 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 #ifdef ENABLE_OPENSSL /* empty file without openssl */ #include #include #include "internal.h" #include "asn1.h" #include "cardctl.h" #include "opensc.h" #include "pkcs15.h" #include "iso7816.h" /* #include "hash-strings.h" */ #include "authentic.h" #include #define AUTHENTIC_CARD_DEFAULT_FLAGS ( 0 \ | SC_ALGORITHM_ONBOARD_KEY_GEN \ | SC_ALGORITHM_RSA_PAD_ISO9796 \ | SC_ALGORITHM_RSA_PAD_PKCS1 \ | SC_ALGORITHM_RSA_HASH_NONE \ | SC_ALGORITHM_RSA_HASH_SHA1 \ | SC_ALGORITHM_RSA_HASH_SHA256) #define AUTHENTIC_READ_BINARY_LENGTH_MAX 0xE7 /* generic iso 7816 operations table */ static const struct sc_card_operations *iso_ops = NULL; /* our operations table with overrides */ static struct sc_card_operations authentic_ops; static struct sc_card_driver authentic_drv = { "Oberthur AuthentIC v3.1", "authentic", &authentic_ops, NULL, 0, NULL }; /* * 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_cplc cplc; }; 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 } }; unsigned char aid_AuthentIC_3_2[] = { 0xA0,0x00,0x00,0x00,0x77,0x01,0x00,0x70,0x0A,0x10,0x00,0xF1,0x00,0x00,0x01,0x00 }; 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, struct sc_acl_entry *acls); static int authentic_pin_is_verified(struct sc_card *card, struct sc_pin_cmd_data *pin_cmd, int *tries_left); 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); static int authentic_sm_get_wrapped_apdu(struct sc_card *card, struct sc_apdu *apdu, struct sc_apdu **sm_apdu); static int authentic_sm_free_wrapped_apdu(struct sc_card *card, struct sc_apdu *apdu, struct sc_apdu **sm_apdu); #endif static int authentic_update_blob(struct sc_context *ctx, unsigned tag, unsigned char *data, size_t data_len, unsigned char **blob, size_t *blob_size) { unsigned char *pp = NULL; int offs = 0; size_t sz; if (data_len == 0) return SC_SUCCESS; sz = data_len + 2; if (tag > 0xFF) sz++; if (data_len > 0x7F && data_len < 0x100) sz++; else if (data_len >= 0x100) sz += 2; pp = realloc(*blob, *blob_size + sz); if (!pp) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); if (tag > 0xFF) *(pp + *blob_size + offs++) = (tag >> 8) & 0xFF; *(pp + *blob_size + offs++) = tag & 0xFF; if (data_len >= 0x100) { *(pp + *blob_size + offs++) = 0x82; *(pp + *blob_size + offs++) = (data_len >> 8) & 0xFF; } else if (data_len > 0x7F) { *(pp + *blob_size + offs++) = 0x81; } *(pp + *blob_size + offs++) = data_len & 0xFF; memcpy(pp + *blob_size + offs, data, data_len); *blob_size += sz; *blob = pp; return SC_SUCCESS; } static int authentic_parse_size(unsigned char *in, size_t in_len, size_t *out) { if (!in || !out || in_len < 1) return SC_ERROR_INVALID_ARGUMENTS; if (*in < 0x80) { *out = *in; 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; } return SC_ERROR_INVALID_DATA; } static int authentic_get_tagged_data(struct sc_context *ctx, unsigned char *in, size_t in_len, 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) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); 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; } else { tag = *(in + offs); tag_len = 1; } 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; return SC_SUCCESS; } offs += tag_len + size_len + size; } return SC_ERROR_ASN1_OBJECT_NOT_FOUND; } static int authentic_decode_pubkey_rsa(struct sc_context *ctx, unsigned char *blob, size_t blob_len, struct sc_pkcs15_prkey **out_key) { struct sc_pkcs15_prkey_rsa *key; unsigned char *data; size_t data_len; int rv; LOG_FUNC_CALLED(ctx); if (!out_key) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); if (!(*out_key)) { *out_key = calloc(1, sizeof(struct sc_pkcs15_prkey)); if (!(*out_key)) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot callocate pkcs15 private key"); (*out_key)->algorithm = SC_ALGORITHM_RSA; } else if (*out_key && (*out_key)->algorithm != SC_ALGORITHM_RSA) { LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); } key = &(*out_key)->u.rsa; rv = authentic_get_tagged_data(ctx, blob, blob_len, AUTHENTIC_TAG_RSA_PUBLIC, &data, &data_len); LOG_TEST_RET(ctx, rv, "cannot get public key SDO data"); blob = data; blob_len = data_len; /* Get RSA public modulus */ rv = authentic_get_tagged_data(ctx, blob, blob_len, AUTHENTIC_TAG_RSA_PUBLIC_MODULUS, &data, &data_len); LOG_TEST_RET(ctx, rv, "cannot get public key SDO data"); if (key->modulus.data) free(key->modulus.data); key->modulus.data = calloc(1, data_len); if (!key->modulus.data) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot callocate modulus BN"); memcpy(key->modulus.data, data, data_len); key->modulus.len = data_len; /* Get RSA public exponent */ rv = authentic_get_tagged_data(ctx, blob, blob_len, AUTHENTIC_TAG_RSA_PUBLIC_EXPONENT, &data, &data_len); LOG_TEST_RET(ctx, rv, "cannot get public key SDO data"); if (key->exponent.data) free(key->exponent.data); key->exponent.data = calloc(1, data_len); if (!key->exponent.data) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot callocate modulus BN"); memcpy(key->exponent.data, data, data_len); key->exponent.len = data_len; LOG_FUNC_RETURN(ctx, rv); } static int authentic_parse_credential_data(struct sc_context *ctx, struct sc_pin_cmd_data *pin_cmd, struct sc_acl_entry *acls, unsigned char *blob, size_t blob_len) { unsigned char *data; size_t data_len; int rv, ii; unsigned tag = AUTHENTIC_TAG_CREDENTIAL | pin_cmd->pin_reference; rv = authentic_get_tagged_data(ctx, blob, blob_len, tag, &blob, &blob_len); LOG_TEST_RET(ctx, rv, "cannot get credential data"); rv = authentic_get_tagged_data(ctx, blob, blob_len, AUTHENTIC_TAG_CREDENTIAL_TRYLIMIT, &data, &data_len); LOG_TEST_RET(ctx, rv, "cannot get try limit"); pin_cmd->pin1.max_tries = *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) pin_cmd->pin_type = SC_AC_CHV; else if (*data >= 2 && *data <= 7) pin_cmd->pin_type = SC_AC_AUT; else LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "unsupported Credential type"); /* 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; } } } } rv = authentic_get_tagged_data(ctx, blob, blob_len, AUTHENTIC_TAG_CREDENTIAL_PINPOLICY, &data, &data_len); if (!rv) { blob = data; blob_len = data_len; rv = authentic_get_tagged_data(ctx, blob, blob_len, AUTHENTIC_TAG_CREDENTIAL_PINPOLICY_MAXLENGTH, &data, &data_len); LOG_TEST_RET(ctx, rv, "failed to get PIN max.length value"); pin_cmd->pin1.max_length = *data; rv = authentic_get_tagged_data(ctx, blob, blob_len, AUTHENTIC_TAG_CREDENTIAL_PINPOLICY_MINLENGTH, &data, &data_len); LOG_TEST_RET(ctx, rv, "failed to get PIN min.length value"); pin_cmd->pin1.min_length = *data; } return SC_SUCCESS; } static int authentic_get_cplc(struct sc_card *card) { struct authentic_private_data *prv_data = (struct authentic_private_data *) card->drv_data; struct sc_apdu apdu; int rv, ii; unsigned char p1, p2; p1 = (SC_CPLC_TAG >> 8) & 0xFF; p2 = SC_CPLC_TAG & 0xFF; sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xCA, p1, p2); for (ii=0;ii<2;ii++) { apdu.le = SC_CPLC_DER_SIZE; apdu.resplen = sizeof(prv_data->cplc.value); apdu.resp = prv_data->cplc.value; rv = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, rv, "APDU transmit failed"); rv = sc_check_sw(card, apdu.sw1, apdu.sw2); if (rv != SC_ERROR_CLASS_NOT_SUPPORTED) break; apdu.cla = 0x80; } LOG_TEST_RET(card->ctx, rv, "'GET CPLC' error"); prv_data->cplc.len = SC_CPLC_DER_SIZE; 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_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"); return 0; } sc_log(ctx, "'%s' card matched", authentic_known_atrs[i].name); return 1; } static int authentic_init_oberthur_authentic_3_2(struct sc_card *card) { struct sc_context *ctx = card->ctx; unsigned int flags; int rv = 0; LOG_FUNC_CALLED(ctx); flags = AUTHENTIC_CARD_DEFAULT_FLAGS; card->caps = SC_CARD_CAP_RNG; card->caps |= SC_CARD_CAP_APDU_EXT; card->caps |= SC_CARD_CAP_USE_FCI_AC; #ifdef ENABLE_SM card->sm_ctx.ops.open = authentic_sm_open; card->sm_ctx.ops.get_sm_apdu = authentic_sm_get_wrapped_apdu; 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); 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); } static int authentic_init(struct sc_card *card) { struct sc_context *ctx = card->ctx; int ii, rv = SC_ERROR_INVALID_CARD; LOG_FUNC_CALLED(ctx); for(ii=0;authentic_known_atrs[ii].atr;ii++) { if (card->type == authentic_known_atrs[ii].type) { card->name = authentic_known_atrs[ii].name; card->flags = authentic_known_atrs[ii].flags; break; } } if (!authentic_known_atrs[ii].atr) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_CARD); card->cla = 0x00; 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 != 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) { struct sc_context *ctx = card->ctx; int rv; unsigned char *buf_zero = NULL; LOG_FUNC_CALLED(ctx); 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_FUNC_RETURN(ctx, rv); } static int authentic_set_current_files(struct sc_card *card, struct sc_path *path, unsigned char *resp, size_t resplen, struct sc_file **file_out) { struct sc_context *ctx = card->ctx; struct sc_file *file = NULL; int rv; LOG_FUNC_CALLED(ctx); if (resplen) { switch (resp[0]) { case 0x62: case 0x6F: file = sc_file_new(); if (file == NULL) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); if (path) file->path = *path; rv = authentic_process_fci(card, file, resp, resplen); 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) { if (cur_df_path.len + card->cache.current_df->path.len > sizeof card->cache.current_df->path.value || cur_df_path.len > sizeof card->cache.current_df->path.value) { sc_file_free(file); LOG_FUNC_RETURN(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); } memmove(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 sc_file_free(file); } LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int authentic_select_mf(struct sc_card *card, struct sc_file **file_out) { struct sc_context *ctx = card->ctx; struct sc_path mfpath; int rv; struct sc_apdu apdu; unsigned char rbuf[SC_MAX_APDU_BUFFER_SIZE]; LOG_FUNC_CALLED(ctx); 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; apdu.resplen = sizeof(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, "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 - 2)); memcpy(in_path.value, "\x3F\x00", 2); in_path.len += 2; } for (offs = 0; (offs + 1) < in_path.len && (offs + 1) < 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, (int)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,len=%"SC_FORMAT_LEN_SIZE_T"u) %s", path->type, path->len, 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) { struct sc_context *ctx = card->ctx; struct sc_apdu apdu; struct sc_path lpath; unsigned char rbuf[SC_MAX_APDU_BUFFER_SIZE]; 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 == 0) { LOG_FUNC_RETURN(ctx, SC_SUCCESS); } else if (file_out != NULL) { sc_file_free(*file_out); *file_out = NULL; } } 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); if (card->type != SC_CARD_TYPE_OBERTHUR_AUTHENTIC_3_2) LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Unsupported card"); if (lpath.type == SC_PATH_TYPE_FILE_ID) { apdu.p1 = 0x00; } else if (lpath.type == SC_PATH_TYPE_PATH) { apdu.p1 = 0x08; } else if (lpath.type == SC_PATH_TYPE_FROM_CURRENT) { apdu.p1 = 0x09; } else if (lpath.type == SC_PATH_TYPE_DF_NAME) { apdu.p1 = 4; } else if (lpath.type == SC_PATH_TYPE_PARENT) { apdu.p1 = 0x03; pathlen = 0; apdu.cse = SC_APDU_CASE_2_SHORT; } else { sc_log(ctx, "Invalid PATH type: 0x%X", lpath.type); LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "authentic_select_file() invalid PATH type"); } apdu.lc = pathlen; apdu.data = lpath.value; apdu.datalen = pathlen; if (apdu.cse == SC_APDU_CASE_4_SHORT || apdu.cse == SC_APDU_CASE_2_SHORT) { apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = 256; } 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, "authentic_select_file() check SW failed"); rv = authentic_set_current_files(card, &lpath, apdu.resp, apdu.resplen, file_out); LOG_TEST_RET(ctx, rv, "authentic_select_file() cannot set 'current_file'"); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int authentic_read_binary(struct sc_card *card, unsigned int idx, unsigned char *buf, size_t count, unsigned long *flags) { struct sc_context *ctx = card->ctx; 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); rest = count; while(rest) { sz = rest > 256 ? 256 : rest; 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 (rv) { LOG_TEST_RET(ctx, SC_ERROR_INTERNAL, "authentic_read_binary() failed"); LOG_FUNC_RETURN(ctx, (int)count); } rv = sc_check_sw(card, apdu.sw1, apdu.sw2); if (!rv) count = ret_count; LOG_TEST_RET(ctx, rv, "authentic_read_binary() failed"); LOG_FUNC_RETURN(ctx, (int)count); } static int 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 apdu; size_t sz, rest; 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); rest = count; while(rest) { sz = rest > 255 ? 255 : 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 (rv) { LOG_TEST_RET(ctx, SC_ERROR_INTERNAL, "authentic_write_binary() failed"); LOG_FUNC_RETURN(ctx, (int)count); } rv = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(ctx, rv, "authentic_write_binary() failed"); LOG_FUNC_RETURN(ctx, (int)count); } static int 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 apdu; size_t sz, rest; 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); rest = count; while(rest) { sz = rest > 255 ? 255 : 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 (rv) { LOG_TEST_RET(ctx, SC_ERROR_INTERNAL, "authentic_update_binary() failed"); LOG_FUNC_RETURN(ctx, (int)count); } rv = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(ctx, rv, "authentic_update_binary() failed"); LOG_FUNC_RETURN(ctx, (int)count); } static int authentic_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; unsigned ii; const unsigned char *tag = NULL; unsigned char ops_DF[8] = { SC_AC_OP_CREATE, SC_AC_OP_DELETE, SC_AC_OP_CRYPTO, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; unsigned char ops_EF[8] = { SC_AC_OP_READ, SC_AC_OP_DELETE, SC_AC_OP_UPDATE, SC_AC_OP_RESIZE, 0xFF, 0xFF, 0xFF, 0xFF }; LOG_FUNC_CALLED(ctx); tag = sc_asn1_find_tag(card->ctx, buf, buflen, 0x6F, &taglen); if (tag != NULL) { sc_log(ctx, " FCP length %"SC_FORMAT_LEN_SIZE_T"u", taglen); buf = tag; buflen = taglen; } tag = sc_asn1_find_tag(card->ctx, buf, buflen, 0x62, &taglen); if (tag != NULL) { sc_log(ctx, " FCP length %"SC_FORMAT_LEN_SIZE_T"u", taglen); buf = tag; buflen = taglen; } rv = iso_ops->process_fci(card, file, buf, buflen); LOG_TEST_RET(ctx, rv, "ISO parse FCI failed"); if (!file->sec_attr_len) { 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; } else { LOG_TEST_RET(ctx, SC_ERROR_OBJECT_NOT_FOUND, "ACLs tag missing"); } } 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); unsigned sc = acl * 0x100 + cred_id; sc_log(ctx, "ACL(%i) op 0x%X, acl %X:%X", ii, op, acl, cred_id); if (op == 0xFF) ; else if (!acl && !cred_id) sc_file_add_acl_entry(file, op, SC_AC_NONE, 0); else if (acl == 0xFF) sc_file_add_acl_entry(file, op, SC_AC_NEVER, 0); else if (acl & AUTHENTIC_AC_SM_MASK) sc_file_add_acl_entry(file, op, SC_AC_SCB, sc); else if (cred_id) sc_file_add_acl_entry(file, op, SC_AC_CHV, cred_id); else sc_file_add_acl_entry(file, op, SC_AC_NEVER, 0); } LOG_FUNC_RETURN(ctx, 0); } static int authentic_fcp_encode(struct sc_card *card, struct sc_file *file, unsigned char *out, size_t out_len) { struct sc_context *ctx = card->ctx; unsigned char buf[0x80]; size_t ii, offs; unsigned char ops_ef[4] = { SC_AC_OP_READ, SC_AC_OP_DELETE, SC_AC_OP_UPDATE, SC_AC_OP_RESIZE }; unsigned char ops_df[3] = { SC_AC_OP_CREATE, SC_AC_OP_DELETE, SC_AC_OP_CRYPTO }; unsigned char *ops = file->type == SC_FILE_TYPE_DF ? ops_df : ops_ef; size_t ops_len = file->type == SC_FILE_TYPE_DF ? 3 : 4; LOG_FUNC_CALLED(ctx); offs = 0; buf[offs++] = ISO7816_TAG_FCP_SIZE; buf[offs++] = 2; buf[offs++] = (file->size >> 8) & 0xFF; buf[offs++] = file->size & 0xFF; buf[offs++] = ISO7816_TAG_FCP_TYPE; buf[offs++] = 1; buf[offs++] = file->type == SC_FILE_TYPE_DF ? ISO7816_FILE_TYPE_DF : ISO7816_FILE_TYPE_TRANSPARENT_EF; buf[offs++] = ISO7816_TAG_FCP_FID; buf[offs++] = 2; buf[offs++] = (file->id >> 8) & 0xFF; buf[offs++] = file->id & 0xFF; buf[offs++] = ISO7816_TAG_FCP_ACLS; buf[offs++] = ops_len * 2; for (ii=0; ii < ops_len; ii++) { const struct sc_acl_entry *entry; entry = sc_file_get_acl_entry(file, ops[ii]); sc_log(ctx, "acl entry(method:%X,ref:%X)", entry->method, entry->key_ref); if (entry->method == SC_AC_NEVER) { /* TODO: After development change for 0xFF */ buf[offs++] = 0x00; buf[offs++] = 0x00; } else if (entry->method == SC_AC_NONE) { buf[offs++] = 0x00; buf[offs++] = 0x00; } else if (entry->method == SC_AC_CHV) { if (!(entry->key_ref & AUTHENTIC_V3_CREDENTIAL_ID_MASK) || (entry->key_ref & ~AUTHENTIC_V3_CREDENTIAL_ID_MASK)) LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Non supported Credential Reference"); buf[offs++] = 0x00; buf[offs++] = 0x01 << (entry->key_ref - 1); } else LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Non supported AC method"); } if (out) { if (out_len < offs) LOG_TEST_RET(ctx, SC_ERROR_BUFFER_TOO_SMALL, "Buffer too small to encode FCP"); memcpy(out, buf, offs); } LOG_FUNC_RETURN(ctx, (int)offs); } static int authentic_create_file(struct sc_card *card, struct sc_file *file) { struct sc_context *ctx = card->ctx; struct sc_apdu apdu; unsigned char sbuf[0x100]; size_t sbuf_len; struct sc_path path; int rv; LOG_FUNC_CALLED(ctx); 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); 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); if (!entry) LOG_FUNC_RETURN(ctx, SC_ERROR_INTERNAL); 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; apdu.lc = sbuf_len + 2; 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, "authentic_create_file() create file error"); path = file->path; memcpy(path.value, path.value + path.len - 2, 2); path.len = 2; rv = authentic_set_current_files(card, &path, sbuf, sbuf_len + 2, NULL); LOG_TEST_RET(ctx, rv, "authentic_select_file() cannot set 'current_file'"); LOG_FUNC_RETURN(ctx, rv); } static int authentic_delete_file(struct sc_card *card, const struct sc_path *path) { struct sc_context *ctx = card->ctx; struct sc_apdu apdu; unsigned char p1; int rv, ii; LOG_FUNC_CALLED(ctx); if (!path) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); for (ii=0, p1 = 0x02; ii<2; ii++, p1 = 0x01) { sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE4, p1, 0x00); apdu.data = path->value + path->len - 2; apdu.datalen = 2; apdu.lc = 2; rv = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(ctx, rv, "APDU transmit failed"); rv = sc_check_sw(card, apdu.sw1, apdu.sw2); if (rv != SC_ERROR_FILE_NOT_FOUND || p1 != 0x02) break; } 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) { struct sc_context *ctx = card->ctx; unsigned char buffer[0x100]; struct sc_pin_cmd_pin *pin1 = &pin_cmd->pin1; int rv; 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); 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); } pin1->len = pin1->min_length; pin1->max_length = 8; memset(buffer, pin1->pad_char, sizeof(buffer)); pin1->data = buffer; 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); LOG_FUNC_RETURN(ctx, rv); } static int authentic_chv_verify(struct sc_card *card, struct sc_pin_cmd_data *pin_cmd, int *tries_left) { struct sc_context *ctx = card->ctx; struct sc_apdu apdu; struct sc_pin_cmd_pin *pin1 = &pin_cmd->pin1; int rv; LOG_FUNC_CALLED(ctx); 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; 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); pin_len = pin1->pad_length; } sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x20, 0, pin_cmd->pin_reference); apdu.data = pin_buff; apdu.datalen = pin_len; 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); sc_log(ctx, "authentic_chv_verify() authentic_chv_verify_pinpad returned %i", rv); 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 (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); LOG_FUNC_RETURN(ctx, rv); } static int authentic_pin_is_verified(struct sc_card *card, struct sc_pin_cmd_data *pin_cmd_data, int *tries_left) { struct sc_context *ctx = card->ctx; struct sc_pin_cmd_data pin_cmd; 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"); pin_cmd = *pin_cmd_data; pin_cmd.pin1.data = (unsigned char *)""; pin_cmd.pin1.len = 0; rv = authentic_chv_verify(card, &pin_cmd, tries_left); LOG_FUNC_RETURN(ctx, rv); } static int authentic_pin_verify(struct sc_card *card, struct sc_pin_cmd_data *pin_cmd) { struct sc_context *ctx = card->ctx; struct authentic_private_data *prv_data = (struct authentic_private_data *) card->drv_data; unsigned char pin_sha1[SHA_DIGEST_LENGTH]; int rv; LOG_FUNC_CALLED(ctx); 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->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); LOG_FUNC_RETURN(ctx, rv); } if (pin_cmd->pin1.data) SHA1(pin_cmd->pin1.data, pin_cmd->pin1.len, pin_sha1); else SHA1((unsigned char *)"", 0, pin_sha1); if (!memcmp(pin_sha1, prv_data->pins_sha1[pin_cmd->pin_reference], SHA_DIGEST_LENGTH)) { sc_log(ctx, "Already verified"); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } memset(prv_data->pins_sha1[pin_cmd->pin_reference], 0, sizeof(prv_data->pins_sha1[0])); rv = authentic_pin_get_policy(card, pin_cmd, NULL); LOG_TEST_RET(ctx, rv, "Get 'PIN policy' error"); 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); LOG_TEST_RET(ctx, rv, "PIN CHV verification error"); memcpy(prv_data->pins_sha1[pin_cmd->pin_reference], pin_sha1, SHA_DIGEST_LENGTH); LOG_FUNC_RETURN(ctx, rv); } static int authentic_pin_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[SC_MAX_APDU_BUFFER_SIZE], pin2_data[SC_MAX_APDU_BUFFER_SIZE]; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "CHV PINPAD PIN reference %i", reference); 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); } 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 | SC_PIN_CMD_NEED_PADDING; 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)); pin_cmd.pin1.data = pin1_data; pin_cmd.pin1.len = pin_cmd.pin1.min_length; pin_cmd.pin1.max_length = 8; memcpy(&pin_cmd.pin2, &pin_cmd.pin1, sizeof(pin_cmd.pin1)); memset(pin2_data, pin_cmd.pin2.pad_char, sizeof(pin2_data)); pin_cmd.pin2.data = pin2_data; sc_log(ctx, "PIN1 lengths max/min/pad: %"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.pad_length); sc_log(ctx, "PIN2 lengths 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, tries_left); LOG_FUNC_RETURN(ctx, rv); } static int authentic_pin_change(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_left) { struct sc_context *ctx = card->ctx; struct authentic_private_data *prv_data = (struct authentic_private_data *) card->drv_data; struct sc_apdu apdu; unsigned char pin_data[SC_MAX_APDU_BUFFER_SIZE]; size_t offs; int rv; rv = authentic_pin_get_policy(card, data, NULL); LOG_TEST_RET(ctx, rv, "Get 'PIN policy' error"); 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); 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 > 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) { memcpy(pin_data, data->pin1.data, data->pin1.len); offs += data->pin1.pad_length; } if (data->pin2.data && data->pin2.len) 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; apdu.datalen = offs + data->pin1.pad_length; apdu.lc = offs + data->pin1.pad_length; rv = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(ctx, rv, "APDU transmit failed"); rv = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_FUNC_RETURN(ctx, rv); } static int authentic_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); 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); } 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 | SC_PIN_CMD_NEED_PADDING; 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)); pin_cmd.pin1.data = pin_data; pin_cmd.pin1.len = pin_cmd.pin1.min_length; pin_cmd.pin1.max_length = 8; memcpy(&pin_cmd.pin2, &pin_cmd.pin1, sizeof(pin_cmd.pin1)); memset(&pin_cmd.pin1, 0, sizeof(pin_cmd.pin1)); sc_log(ctx, "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); LOG_FUNC_RETURN(ctx, rv); } static int 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; unsigned char rbuf[0x100]; int ii, rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "get PIN(type:%X,ref:%X,tries-left:%i)", data->pin_type, data->pin_reference, data->pin1.tries_left); sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xCA, 0x5F, data->pin_reference); for (ii=0;ii<2;ii++) { apdu.le = sizeof(rbuf); apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); rv = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(ctx, rv, "APDU transmit failed"); rv = sc_check_sw(card, apdu.sw1, apdu.sw2); if (rv != SC_ERROR_CLASS_NOT_SUPPORTED) break; apdu.cla = 0x80; } LOG_TEST_RET(ctx, rv, "'GET DATA' error"); data->pin1.tries_left = -1; 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; data->pin1.offset = 5; data->pin1.pad_char = 0xFF; data->pin1.pad_length = data->pin1.max_length; data->pin1.logged_in = SC_PIN_STATE_UNKNOWN; data->flags |= SC_PIN_CMD_NEED_PADDING; sc_log(ctx, "PIN policy: size max/min/pad %"SC_FORMAT_LEN_SIZE_T"u/%"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.pad_length, data->pin1.max_tries, data->pin1.tries_left); LOG_FUNC_RETURN(ctx, rv); } static int authentic_pin_reset(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_left) { 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 %zu/%zu)", data->pin_reference, data->pin1.len, data->pin2.len); memset(prv_data->pins_sha1[data->pin_reference], 0, sizeof(prv_data->pins_sha1[0])); memset(&pin_cmd, 0, sizeof(pin_cmd)); pin_cmd.pin_reference = data->pin_reference; pin_cmd.pin_type = data->pin_type; pin_cmd.pin1.tries_left = -1; memset(&acls, 0, sizeof(acls)); rv = authentic_pin_get_policy(card, &pin_cmd, acls); LOG_TEST_RET(ctx, rv, "Get 'PIN policy' error"); if (acls[AUTHENTIC_ACL_NUM_PIN_RESET].method == SC_AC_CHV) { for (ii=0;ii<8;ii++) { unsigned char mask = 0x01 << ii; 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, NULL); LOG_TEST_RET(ctx, rv, "Get 'PIN policy' error"); if (puk_cmd.pin_type == SC_AC_CHV) break; } } if (ii < 8) { puk_cmd.pin1.data = data->pin1.data; puk_cmd.pin1.len = data->pin1.len; 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"); } } reference = data->pin_reference; if (data->pin2.len) { unsigned char pin_data[SC_MAX_APDU_BUFFER_SIZE]; memset(pin_data, pin_cmd.pin1.pad_char, sizeof(pin_data)); memcpy(pin_data, data->pin2.data, data->pin2.len); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x2C, 0x02, reference); apdu.data = pin_data; apdu.datalen = pin_cmd.pin1.pad_length; apdu.lc = pin_cmd.pin1.pad_length; 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_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x2C, 3, reference); 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 { rv = authentic_chv_set_pinpad(card, reference); LOG_TEST_RET(ctx, rv, "Failed to set PIN with pin-pad"); } LOG_FUNC_RETURN(ctx, rv); } static int authentic_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_left) { 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:%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); break; case SC_PIN_CMD_UNBLOCK: rv = authentic_pin_reset(card, data, tries_left); break; case SC_PIN_CMD_GET_INFO: 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); } static int authentic_get_serialnr(struct sc_card *card, struct sc_serial_number *serial) { struct sc_context *ctx = card->ctx; struct authentic_private_data *prv_data = (struct authentic_private_data *) card->drv_data; int rv; LOG_FUNC_CALLED(ctx); if (!card->serialnr.len) { rv = authentic_get_cplc(card); LOG_TEST_RET(ctx, rv, "get CPLC data error"); card->serialnr.len = 4; memcpy(card->serialnr.value, prv_data->cplc.value + 15, 4); sc_log(ctx, "serial %02X%02X%02X%02X", card->serialnr.value[0], card->serialnr.value[1], card->serialnr.value[2], card->serialnr.value[3]); } if (serial) memcpy(serial, &card->serialnr, sizeof(*serial)); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int authentic_get_challenge(struct sc_card *card, unsigned char *rnd, size_t len) { /* 'GET CHALLENGE' returns always 24 bytes */ unsigned char rbuf[0x18]; 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 authentic_manage_sdo_encode_prvkey(struct sc_card *card, struct sc_pkcs15_prkey *prvkey, unsigned char **out, size_t *out_len) { struct sc_context *ctx = card->ctx; struct sc_pkcs15_prkey_rsa rsa; unsigned char *blob = NULL, *blob01 = NULL; size_t blob_len = 0, blob01_len = 0; int rv; if (!prvkey || !out || !out_len) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Invalid arguments"); if (prvkey->algorithm != SC_ALGORITHM_RSA) LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Invalid SDO operation"); 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_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_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_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_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_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_GOTO_ERR(ctx, rv, "SDO RSA Private encode error"); free (blob); blob = NULL; blob_len = 0; /* Encode public RSA key part */ sc_log(ctx, "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_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_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_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_GOTO_ERR(ctx, rv, "SDO RSA encode error"); err: free(blob01); free(blob); LOG_FUNC_RETURN(ctx, rv); } static int authentic_manage_sdo_encode(struct sc_card *card, struct sc_authentic_sdo *sdo, unsigned long cmd, unsigned char **out, size_t *out_len) { struct sc_context *ctx = card->ctx; unsigned char *data = NULL; size_t data_len = 0; unsigned char data_tag = AUTHENTIC_TAG_DOCP; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "encode SDO operation (cmd:%lX,mech:%X,id:%X)", cmd, sdo->docp.mech, sdo->docp.id); if (!out || !out_len) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Invalid arguments"); rv = authentic_update_blob(ctx, AUTHENTIC_TAG_DOCP_MECH, &sdo->docp.mech, sizeof(sdo->docp.mech), &data, &data_len); 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_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_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_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_GOTO_ERR(ctx, rv, "DOCP ACLs encode error"); } } else if (cmd == SC_CARDCTL_AUTHENTIC_SDO_STORE) { if (sdo->docp.mech == AUTHENTIC_MECH_CRYPTO_RSA1024 || sdo->docp.mech == AUTHENTIC_MECH_CRYPTO_RSA1280 || sdo->docp.mech == AUTHENTIC_MECH_CRYPTO_RSA1536 || 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_GOTO_ERR(ctx, rv, "SDO RSA encode error"); } else { LOG_TEST_GOTO_ERR(ctx, SC_ERROR_NOT_SUPPORTED, "Cryptographic object unsupported for encoding"); } } else if (cmd == SC_CARDCTL_AUTHENTIC_SDO_GENERATE) { if (sdo->data.prvkey) { 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_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_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_GOTO_ERR(ctx, rv, "SDO DOCP encode error"); sc_log_hex(ctx, "encoded SDO operation data", *out, *out_len); err: free(data); LOG_FUNC_RETURN(ctx, rv); } static int authentic_manage_sdo_generate(struct sc_card *card, struct sc_authentic_sdo *sdo) { struct sc_context *ctx = card->ctx; struct sc_apdu apdu; unsigned char rbuf[0x400]; unsigned char *data = NULL; size_t data_len = 0; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "Generate SDO(mech:%X,id:%X)", sdo->docp.mech, sdo->docp.id); rv = authentic_manage_sdo_encode(card, sdo, SC_CARDCTL_AUTHENTIC_SDO_GENERATE, &data, &data_len); LOG_TEST_RET(ctx, rv, "Cannot encode SDO data"); sc_log(ctx, "encoded SDO length %"SC_FORMAT_LEN_SIZE_T"u", data_len); sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x47, 0x00, 0x00); apdu.data = data; apdu.datalen = data_len; apdu.lc = data_len; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = 0x100; 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, "authentic_sdo_create() SDO put data error"); rv = authentic_decode_pubkey_rsa(ctx, apdu.resp, apdu.resplen, &sdo->data.prvkey); LOG_TEST_RET(card->ctx, rv, "cannot decode public key"); free(data); LOG_FUNC_RETURN(ctx, rv); } static int authentic_manage_sdo(struct sc_card *card, struct sc_authentic_sdo *sdo, unsigned long cmd) { struct sc_context *ctx = card->ctx; struct sc_apdu apdu; unsigned char *data = NULL; size_t data_len = 0, save_max_send = card->max_send_size; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "SDO(cmd:%lX,mech:%X,id:%X)", cmd, sdo->docp.mech, sdo->docp.id); rv = authentic_manage_sdo_encode(card, sdo, cmd, &data, &data_len); LOG_TEST_RET(ctx, rv, "Cannot encode SDO data"); sc_log(ctx, "encoded SDO length %"SC_FORMAT_LEN_SIZE_T"u", data_len); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xDB, 0x3F, 0xFF); apdu.data = data; apdu.datalen = data_len; apdu.lc = data_len; apdu.flags |= SC_APDU_FLAGS_CHAINING; if (card->max_send_size > 255) card->max_send_size = 255; rv = sc_transmit_apdu(card, &apdu); card->max_send_size = save_max_send; 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"); LOG_FUNC_RETURN(ctx, rv); } static int authentic_card_ctl(struct sc_card *card, unsigned long cmd, void *ptr) { struct sc_context *ctx = card->ctx; struct sc_authentic_sdo *sdo = (struct sc_authentic_sdo *) ptr; switch (cmd) { case SC_CARDCTL_GET_SERIALNR: return authentic_get_serialnr(card, (struct sc_serial_number *)ptr); case SC_CARDCTL_AUTHENTIC_SDO_CREATE: sc_log(ctx, "CARDCTL SDO_CREATE: sdo(mech:%X,id:%X)", sdo->docp.mech, sdo->docp.id); return authentic_manage_sdo(card, (struct sc_authentic_sdo *) ptr, cmd); case SC_CARDCTL_AUTHENTIC_SDO_DELETE: sc_log(ctx, "CARDCTL SDO_DELETE: sdo(mech:%X,id:%X)", sdo->docp.mech, sdo->docp.id); return authentic_manage_sdo(card, (struct sc_authentic_sdo *) ptr, cmd); case SC_CARDCTL_AUTHENTIC_SDO_STORE: sc_log(ctx, "CARDCTL SDO_STORE: sdo(mech:%X,id:%X)", sdo->docp.mech, sdo->docp.id); return authentic_manage_sdo(card, (struct sc_authentic_sdo *) ptr, cmd); case SC_CARDCTL_AUTHENTIC_SDO_GENERATE: sc_log(ctx, "CARDCTL SDO_GENERATE: sdo(mech:%X,id:%X)", sdo->docp.mech, sdo->docp.id); return authentic_manage_sdo_generate(card, (struct sc_authentic_sdo *) ptr); } return SC_ERROR_NOT_SUPPORTED; } static int authentic_set_security_env(struct sc_card *card, const struct sc_security_env *env, int se_num) { struct sc_context *ctx = card->ctx; struct sc_apdu apdu; unsigned char cse_crt_dst[] = { 0x80, 0x01, AUTHENTIC_ALGORITHM_RSA_PKCS1, 0x83, 0x01, env->key_ref[0] & ~AUTHENTIC_OBJECT_REF_FLAG_LOCAL, }; unsigned char cse_crt_ct[] = { 0x80, 0x01, AUTHENTIC_ALGORITHM_RSA_PKCS1, 0x83, 0x01, env->key_ref[0] & ~AUTHENTIC_OBJECT_REF_FLAG_LOCAL, }; int rv; LOG_FUNC_CALLED(ctx); 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: sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, AUTHENTIC_TAG_CRT_DST); apdu.data = cse_crt_dst; apdu.datalen = sizeof(cse_crt_dst); apdu.lc = sizeof(cse_crt_dst); break; case SC_SEC_OPERATION_DECIPHER: sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, AUTHENTIC_TAG_CRT_CT); apdu.data = cse_crt_ct; apdu.datalen = sizeof(cse_crt_ct); apdu.lc = sizeof(cse_crt_ct); break; default: LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); } 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, "MSE restore error"); LOG_FUNC_RETURN(ctx, rv); } static int authentic_decipher(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 sc_apdu apdu; unsigned char resp[SC_MAX_APDU_BUFFER_SIZE]; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "crgram_len %"SC_FORMAT_LEN_SIZE_T"u; outlen %"SC_FORMAT_LEN_SIZE_T"u", in_len, out_len); if (!out || !out_len || in_len > SC_MAX_APDU_BUFFER_SIZE) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x80, 0x86); apdu.flags |= SC_APDU_FLAGS_CHAINING; apdu.data = in; apdu.datalen = in_len; apdu.lc = in_len; apdu.resp = resp; apdu.resplen = sizeof(resp); apdu.le = 256; 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, "Card returned error"); if (out_len > apdu.resplen) out_len = apdu.resplen; memcpy(out, apdu.resp, out_len); rv = (int)out_len; LOG_FUNC_RETURN(ctx, rv); } static int authentic_finish(struct sc_card *card) { struct sc_context *ctx = card->ctx; LOG_FUNC_CALLED(ctx); #ifdef ENABLE_SM if (card->sm_ctx.ops.close) card->sm_ctx.ops.close(card); #endif if (card->drv_data) free(card->drv_data); card->drv_data = NULL; LOG_FUNC_RETURN(ctx, SC_SUCCESS); } 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 = authentic_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 authentic_sm_acl_init (struct sc_card *card, struct sm_info *sm_info, int cmd, unsigned char *resp, size_t *resp_len) { struct sc_context *ctx; struct sm_type_params_gp *params_gp; struct sc_remote_data rdata; int rv; if (!card || !sm_info || !resp || !resp_len) return SC_ERROR_INVALID_ARGUMENTS; ctx = card->ctx; params_gp = &sm_info->session.gp.params; if (!card->sm_ctx.module.ops.initialize || !card->sm_ctx.module.ops.get_apdus) LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); if (*resp_len < 28) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); sm_info->cmd = cmd; sm_info->sm_type = SM_TYPE_GP_SCP01; sm_info->card_type = card->type; params_gp->index = 0; /* logical channel */ params_gp->version = 1; params_gp->level = 3; /* Only supported SM level 'ENC & MAC' */ sm_info->serialnr = card->serialnr; sc_remote_data_init(&rdata); rv = card->sm_ctx.module.ops.initialize(ctx, sm_info, &rdata); LOG_TEST_RET(ctx, rv, "SM: INITIALIZE failed"); if (!rdata.length) LOG_FUNC_RETURN(ctx, SC_ERROR_INTERNAL); rv = sc_transmit_apdu(card, &rdata.data->apdu); LOG_TEST_RET(ctx, rv, "transmit APDU failed"); rv = sc_check_sw(card, rdata.data->apdu.sw1, rdata.data->apdu.sw2); LOG_TEST_RET(ctx, rv, "Card returned error"); if (rdata.data->apdu.resplen != 28 || *resp_len < 28) LOG_FUNC_RETURN(ctx, SC_ERROR_INTERNAL); memcpy(resp, rdata.data->apdu.resp, 28); *resp_len = 28; rdata.free(&rdata); LOG_FUNC_RETURN(ctx, rv); } static int authentic_sm_execute (struct sc_card *card, struct sm_info *sm_info, unsigned char *data, size_t data_len, unsigned char *out, size_t len) { struct sc_context *ctx = card->ctx; struct sc_remote_data rdata; int rv, ii; if (!card->sm_ctx.module.ops.get_apdus) LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); sc_remote_data_init(&rdata); rv = card->sm_ctx.module.ops.get_apdus(ctx, sm_info, data, data_len, &rdata); LOG_TEST_RET(ctx, rv, "SM: GET_APDUS failed"); if (!rdata.length) LOG_FUNC_RETURN(ctx, SC_ERROR_INTERNAL); sc_log(ctx, "GET_APDUS: rv %i; rdata length %i", rv, rdata.length); for (ii=0; ii < rdata.length; ii++) { struct sc_apdu *apdu = &((rdata.data + ii)->apdu); if (!apdu->ins) break; rv = sc_transmit_apdu(card, apdu); if (rv < 0) break; rv = sc_check_sw(card, apdu->sw1, apdu->sw2); if (rv < 0) break; } rdata.free(&rdata); LOG_FUNC_RETURN(ctx, rv); } static int authentic_sm_open(struct sc_card *card) { struct sc_context *ctx = card->ctx; unsigned char init_data[SC_MAX_APDU_BUFFER_SIZE]; size_t init_data_len = sizeof(init_data); int rv; LOG_FUNC_CALLED(ctx); memset(&card->sm_ctx.info, 0, sizeof(card->sm_ctx.info)); memcpy(card->sm_ctx.info.config_section, card->sm_ctx.config_section, sizeof(card->sm_ctx.info.config_section)); sc_log(ctx, "SM context config '%s'; SM mode 0x%X", card->sm_ctx.info.config_section, card->sm_ctx.sm_mode); if (card->sm_ctx.sm_mode == SM_MODE_TRANSMIT && card->max_send_size == 0) card->max_send_size = 239; rv = authentic_sm_acl_init (card, &card->sm_ctx.info, SM_CMD_INITIALIZE, init_data, &init_data_len); LOG_TEST_RET(ctx, rv, "authentIC: cannot open SM"); rv = authentic_sm_execute (card, &card->sm_ctx.info, init_data, init_data_len, NULL, 0); LOG_TEST_RET(ctx, rv, "SM: execute failed"); card->sm_ctx.info.cmd = SM_CMD_APDU_TRANSMIT; LOG_FUNC_RETURN(ctx, rv); } static int authentic_sm_free_wrapped_apdu(struct sc_card *card, struct sc_apdu *plain, struct sc_apdu **sm_apdu) { struct sc_context *ctx = card->ctx; LOG_FUNC_CALLED(ctx); if (!sm_apdu) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); if (!(*sm_apdu)) LOG_FUNC_RETURN(ctx, SC_SUCCESS); if (plain) { if (plain->resplen < (*sm_apdu)->resplen) 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; plain->sw2 = (*sm_apdu)->sw2; } if ((*sm_apdu)->data) free((unsigned char *) (*sm_apdu)->data); if ((*sm_apdu)->resp) free((unsigned char *) (*sm_apdu)->resp); free(*sm_apdu); *sm_apdu = NULL; LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int authentic_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; int rv = 0; LOG_FUNC_CALLED(ctx); if (!plain || !sm_apdu) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); sc_log(ctx, "called; CLA:%X, INS:%X, P1:%X, P2:%X, data(%"SC_FORMAT_LEN_SIZE_T"u) %p", plain->cla, plain->ins, plain->p1, plain->p2, plain->datalen, plain->data); *sm_apdu = NULL; if ((plain->cla & 0x04) || (plain->cla==0x00 && plain->ins==0x22) || (plain->cla==0x00 && plain->ins==0x2A) || (plain->cla==0x00 && plain->ins==0x84) || (plain->cla==0x00 && plain->ins==0x88) || (plain->cla==0x00 && plain->ins==0xA4) || (plain->cla==0x00 && plain->ins==0xC0) || (plain->cla==0x00 && plain->ins==0xCA) || (plain->cla==0x80 && plain->ins==0x50) ) { sc_log(ctx, "SM wrap is not applied for this APDU"); LOG_FUNC_RETURN(ctx, SC_ERROR_SM_NOT_APPLIED); } if (card->sm_ctx.sm_mode != SM_MODE_TRANSMIT) LOG_FUNC_RETURN(ctx, SC_ERROR_SM_NOT_INITIALIZED); if (!card->sm_ctx.module.ops.get_apdus) LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); apdu = calloc(1, sizeof(struct sc_apdu)); if (!apdu) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); memcpy((void *)apdu, (void *)plain, sizeof(struct sc_apdu)); apdu->data = calloc (1, plain->datalen + 24); if (!apdu->data) { free(apdu); LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); } if (plain->data && plain->datalen) memcpy((unsigned char *) apdu->data, plain->data, plain->datalen); apdu->resp = calloc (1, plain->resplen + 32); if (!apdu->resp) { free(apdu); LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); } card->sm_ctx.info.cmd = SM_CMD_APDU_TRANSMIT; card->sm_ctx.info.cmd_data = (void *)apdu; rv = card->sm_ctx.module.ops.get_apdus(ctx, &card->sm_ctx.info, NULL, 0, NULL); if (rv < 0) { free(apdu->resp); free(apdu); } LOG_TEST_RET(ctx, rv, "SM: GET_APDUS failed"); *sm_apdu = apdu; LOG_FUNC_RETURN(ctx, SC_SUCCESS); } #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 = authentic_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) { struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); if (!iso_ops) iso_ops = iso_drv->ops; authentic_ops = *iso_ops; authentic_ops.match_card = authentic_match_card; authentic_ops.init = authentic_init; authentic_ops.finish = authentic_finish; authentic_ops.read_binary = authentic_read_binary; authentic_ops.write_binary = authentic_write_binary; authentic_ops.update_binary = authentic_update_binary; authentic_ops.erase_binary = authentic_erase_binary; /* authentic_ops.resize_file = authentic_resize_file; */ authentic_ops.select_file = authentic_select_file; /* get_response: Untested */ authentic_ops.get_challenge = authentic_get_challenge; authentic_ops.set_security_env = authentic_set_security_env; /* decipher: Untested */ authentic_ops.decipher = authentic_decipher; /* authentic_ops.compute_signature = authentic_compute_signature; */ authentic_ops.create_file = authentic_create_file; authentic_ops.delete_file = authentic_delete_file; 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; } struct sc_card_driver * sc_get_authentic_driver(void) { return sc_get_driver(); } #endif /* ENABLE_OPENSSL */ OpenSC-0.26.1/src/libopensc/card-belpic.c000066400000000000000000000344351474147347300200340ustar00rootroot00000000000000/* * card-belpic.c: Support for Belgium EID card * * Copyright (C) 2003, Zetes Belgium * * 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 */ /* About the Belpic (Belgian Personal Identity Card) card * * 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 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. * * 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 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 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 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 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. * * For (2), we decided that a GUI for asking the PIN would be the best * thing to do (another option would be to make 2 virtual slots but that * 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 always be the GUI. * * For (3), we link dynamically against a pin pad lib (DLL) that implements the * 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 * her PIN on the pin pad reader in order to make a legally valid signature. * * Further the (current) Belpic card as quite some limitations: * no key pair generation or update of data except after establishing a Secure * Channel or CTV-authentication (which can only be done at the municipalities), * no encryption. The result is that only a very limited amount of functions * is/had to be implemented to get the pkcs11 library working. * * About the belpic_set_language: the RA-PC software (including the pkcs11 lib) * in the Brussels' communities should be able to change the language of the GUI * messages. So the language set by this function takes priority on all other * language-selection functionality. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "internal.h" #include "log.h" /* To be removed */ #include static long t1, t2, tot_read = 0, tot_dur = 0, dur; #define BELPIC_VERSION "1.4" /* Most of the #defines here are also present in the pkcs15 files, but * because this driver has no access to them, it's hardcoded here. If * other Belpic cards with other 'settings' appear, we'll have to move * these #defines to the struct belpic_priv_data */ #define BELPIC_MAX_FILE_SIZE 65535 #define BELPIC_PIN_BUF_SIZE 8 #define BELPIC_MIN_USER_PIN_LEN 4 #define BELPIC_MAX_USER_PIN_LEN 12 #define BELPIC_PIN_ENCODING SC_PIN_ENCODING_GLP #define BELPIC_PAD_CHAR 0xFF #define BELPIC_KEY_REF_NONREP 0x83 /* Data in the return value for the GET CARD DATA command: * All fields are one byte, except when noted otherwise. * * 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. */ // Card serial number (16 bytes) #define BELPIC_CARDDATA_OFF_SERIALNUM 0 // "Component code" #define BELPIC_CARDDATA_OFF_COMPCODE 16 // "OS number" #define BELPIC_CARDDATA_OFF_OSNUM 17 // "OS version" #define BELPIC_CARDDATA_OFF_OSVER 18 // "Softmask number" #define BELPIC_CARDDATA_OFF_SMNUM 19 // "Softmask version" #define BELPIC_CARDDATA_OFF_SMVER 20 // Applet version #define BELPIC_CARDDATA_OFF_APPLETVERS 21 // Global OS version (2 bytes) #define BELPIC_CARDDATA_OFF_GL_OSVE 22 // Applet interface version #define BELPIC_CARDDATA_OFF_APPINTVERS 24 // PKCS#1 support version #define BELPIC_CARDDATA_OFF_PKCS1 25 // Key exchange version #define BELPIC_CARDDATA_OFF_KEYX 26 // Applet life cycle (Should always be 0F for released cards, is 07 when not issued yet) #define BELPIC_CARDDATA_OFF_APPLCYCLE 27 // Full length of reply #define BELPIC_CARDDATA_RESP_LEN 28 /* Used for a trick in select file and read binary */ static size_t next_idx = (size_t)-1; static const struct sc_atr_table belpic_atrs[] = { /* Applet V1.8 -- disabled, as it requires driver updates which are not yet implemented */ /* { "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 } }; static struct sc_card_operations belpic_ops; static struct sc_card_driver belpic_drv = { "Belpic cards", "belpic", &belpic_ops, NULL, 0, NULL }; static const struct sc_card_operations *iso_ops = NULL; 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); r = sc_bytes2apdu(card->ctx, carddata_cmd, sizeof(carddata_cmd), &apdu); if(r) { sc_log(card->ctx, "bytes to APDU conversion failed: %d\n", r); return r; } apdu.resp = carddata_loc; apdu.resplen = carddataloc_len; r = sc_transmit_apdu(card, &apdu); if(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_log(card->ctx, "GetCardData: card returned %d\n", r); return r; } if(apdu.resplen < carddataloc_len) { 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; } return 0; } static int belpic_match_card(sc_card_t *card) { int i; i = _sc_match_atr(card, belpic_atrs, &card->type); if (i < 0) return 0; return 1; } static int belpic_init(sc_card_t *card) { int key_size = 1024; 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 */ card->cla = 0x00; if (card->type == SC_CARD_TYPE_BELPIC_EID) { u8 carddata[BELPIC_CARDDATA_RESP_LEN]; memset(carddata, 0, sizeof(carddata)); if(get_carddata(card, carddata, sizeof(carddata)) < 0) { return SC_ERROR_INVALID_CARD; } 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); } /* State that we have an RNG */ card->caps |= SC_CARD_CAP_RNG; card->max_pin_len = BELPIC_MAX_USER_PIN_LEN; return 0; } static int belpic_select_file(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file_out) { sc_apdu_t apdu; u8 pathbuf[SC_MAX_PATH_SIZE], *path = pathbuf; int r; size_t pathlen; sc_file_t *file = NULL; assert(card != NULL && in_path != NULL); memcpy(path, in_path->value, in_path->len); pathlen = in_path->len; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 0x08, 0x0C); apdu.lc = pathlen; apdu.data = path; apdu.datalen = pathlen; apdu.resplen = 0; apdu.le = 0; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "Select File APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); next_idx = (size_t)-1; /* reset */ if (file_out != NULL) { file = sc_file_new(); file->path = *in_path; if (pathlen >= 2) file->id = (in_path->value[pathlen - 2] << 8) | in_path->value[pathlen - 1]; file->size = BELPIC_MAX_FILE_SIZE; file->shareable = 1; file->ef_structure = SC_FILE_EF_TRANSPARENT; if (pathlen == 2 && memcmp("\x3F\x00", in_path->value, 2) == 0) file->type = SC_FILE_TYPE_DF; else file->type = SC_FILE_TYPE_WORKING_EF; *file_out = file; } return 0; } static int belpic_read_binary(sc_card_t *card, unsigned int idx, u8 * buf, size_t count, unsigned long *flags) { int r; if (next_idx == idx) return 0; /* File was already read entirely */ t1 = clock(); r = iso_ops->read_binary(card, idx, buf, count, flags); t2 = clock(); /* If the 'next_idx trick' shouldn't work, we hope this error * means that an attempt was made to read beyond the file's * contents, so we'll return 0 to end the loop in sc_read_binary()*/ if (r == SC_ERROR_INCORRECT_PARAMETERS) return 0; if (r >= 0 && (size_t)r < count) next_idx = idx + (size_t)r; dur = t2 - t1; tot_dur += dur; tot_read += r; return r; } static int belpic_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left) { 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_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]; int r; 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: apdu.p1 = 0x41; 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_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 { sc_log(card->ctx, "Set Sec Env: unsupported algo 0X%0lX\n", env->algorithm_flags); return SC_ERROR_INVALID_ARGUMENTS; } sbuf[3] = 0x84; /* tag for private key reference */ sbuf[4] = *env->key_ref; /* key reference */ apdu.lc = 5; apdu.datalen = 5; break; default: return SC_ERROR_INVALID_ARGUMENTS; } apdu.le = 0; apdu.data = sbuf; apdu.resplen = 0; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "Set Security Env APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); 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 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. * It's not a problem either, because this function is (for pkcs11) only called * by sc_pkcs15_compute_signature(), where the card is already locked, and * the next function to be executed will be the compute_signature function. */ if (*env->key_ref == BELPIC_KEY_REF_NONREP) { sc_log(card->ctx, "No GUI for NonRep key present, signature cancelled\n"); return SC_ERROR_NOT_SUPPORTED; } return r; } static struct sc_card_driver *sc_get_driver(void) { if (iso_ops == NULL) iso_ops = sc_get_iso7816_driver()->ops; belpic_ops.match_card = belpic_match_card; belpic_ops.init = belpic_init; 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 = 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; return &belpic_drv; } #if 1 struct sc_card_driver *sc_get_belpic_driver(void) { return sc_get_driver(); } #endif OpenSC-0.26.1/src/libopensc/card-cac-common.c000066400000000000000000000061031474147347300206010ustar00rootroot00000000000000/* * 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; } OpenSC-0.26.1/src/libopensc/card-cac-common.h000066400000000000000000000060061474147347300206100ustar00rootroot00000000000000/* * 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 */ OpenSC-0.26.1/src/libopensc/card-cac.c000066400000000000000000001535151474147347300173250ustar00rootroot00000000000000/* * card-cac.c: Support for CAC from NIST SP800-73 * 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_MAX_CHUNK_SIZE 240 #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_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 #define CAC_TAG_CARDURL 0xF3 #define CAC_TAG_PKCS15 0xF4 #define CAC_TAG_ACCESS_CONTROL 0xF6 #define CAC_TAG_DATA_MODEL 0xF5 #define CAC_TAG_CARD_APDU 0xF7 #define CAC_TAG_REDIRECTION 0xFA #define CAC_TAG_CAPABILITY_TUPLES 0xFB #define CAC_TAG_STATUS_TUPLES 0xFC #define CAC_TAG_NEXT_CCC 0xFD #define CAC_TAG_ERROR_CODES 0xFE #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 */ typedef struct cac_access_profile { u8 GCACR_listID; u8 GCACR_readTagListACRID; u8 GCACR_updatevalueACRID; u8 GCACR_readvalueACRID; u8 GCACR_createACRID; u8 GCACR_deleteACRID; u8 CryptoACR_listID; u8 CryptoACR_getChallengeACRID; u8 CryptoACR_internalAuthenicateACRID; u8 CryptoACR_pkiComputeACRID; u8 CryptoACR_readTagListACRID; u8 CryptoACR_updatevalueACRID; u8 CryptoACR_readvalueACRID; u8 CryptoACR_createACRID; u8 CryptoACR_deleteACRID; } cac_access_profile_t; /* part of the card url */ typedef struct cac_access_key_info { u8 keyFileID[2]; u8 keynumber; } cac_access_key_info_t; typedef struct cac_card_url { u8 rid[5]; u8 cardApplicationType; u8 objectID[2]; u8 applicationID[2]; cac_access_profile_t accessProfile; u8 pinID; /* not used for VM cards */ cac_access_key_info_t accessKeyInfo; /* not used for VM cards */ u8 keyCryptoAlgorithm; /* not used for VM cards */ } cac_card_url_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; typedef struct { size_t num_objects; cac_properties_object_t objects[CAC_MAX_OBJECTS]; } cac_properties_t; /* * Flags for Current Selected Object Type * CAC files are TLV files, with TL and V separated. For generic * containers we reintegrate the TL anv V portions into a single * file to read. Certs are also TLV files, but pkcs15 wants the * actual certificate. At select time we know the patch which tells * us what time of files we want to read. We remember that type * so that read_binary can do the appropriate processing. */ #define CAC_OBJECT_TYPE_CERT 1 #define CAC_OBJECT_TYPE_TLV_FILE 4 #define CAC_OBJECT_TYPE_GENERIC 5 /* * Set up the normal CAC paths */ #define CAC_2_RID "\xA0\x00\x00\x01\x16" static const sc_path_t cac_ACA_Path = { "", 0, 0,0,SC_PATH_TYPE_DF_NAME, { CAC_TO_AID(CAC_1_RID "\x10\x00") } }; 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 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_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", 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_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 */ static const cac_object_t *cac_find_obj_by_id(unsigned short object_id) { int i; for (i = 0; i < cac_object_count; i++) { if (cac_objects[i].fd == object_id) { return &cac_objects[i]; } } return NULL; } /* * Lookup the path in the pki list to see if it is a cert path */ static int cac_is_cert(cac_private_data_t * priv, const sc_path_t *in_path) { cac_object_t test_obj; test_obj.path = *in_path; test_obj.path.index = 0; test_obj.path.count = 0; return (list_contains(&priv->pki_list, &test_obj) != 0); } /* * Send a command and receive data. * * 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. * * modelled after a similar function in card-piv.c */ static int cac_apdu_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 = {0}; u8 rbufinitbuf[CAC_MAX_SIZE]; u8 *rbuf; size_t rbuflen; unsigned int apdu_case = SC_APDU_CASE_1; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); 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); rbuf = rbufinitbuf; rbuflen = sizeof(rbufinitbuf); /* if caller provided a buffer and length */ if (recvbuf && *recvbuf && recvbuflen && *recvbuflen) { rbuf = *recvbuf; rbuflen = *recvbuflen; } if (recvbuf) { if (sendbuf) apdu_case = SC_APDU_CASE_4_SHORT; else apdu_case = SC_APDU_CASE_2_SHORT; } else if (sendbuf) apdu_case = SC_APDU_CASE_3_SHORT; sc_format_apdu(card, &apdu, apdu_case, ins, p1, p2); apdu.lc = sendbuflen; apdu.datalen = sendbuflen; apdu.data = sendbuf; if (recvbuf) { apdu.resp = rbuf; apdu.le = (rbuflen > 255) ? 255 : rbuflen; apdu.resplen = rbuflen; } else { apdu.resp = rbuf; apdu.le = 0; apdu.resplen = 0; } 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_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_log(card->ctx, "Transmit failed"); goto err; } r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r < 0) { sc_log(card->ctx, "Card returned error "); goto err; } if (recvbuflen) { if (recvbuf && *recvbuf == NULL) { *recvbuf = malloc(apdu.resplen); if (*recvbuf == NULL) { r = SC_ERROR_OUT_OF_MEMORY; goto err; } memcpy(*recvbuf, rbuf, apdu.resplen); } *recvbuflen = apdu.resplen; r = (int)*recvbuflen; } err: 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; } /* * Read a CAC TLV file. Parameters specify if the TLV file is TL (Tag/Length) file or a V (value) file */ #define HIGH_BYTE_OF_SHORT(x) (((x)>> 8) & 0xff) #define LOW_BYTE_OF_SHORT(x) ((x) & 0xff) 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] = {0}; u8 *out = NULL; u8 *out_ptr = NULL; size_t offset = 0; size_t size = 0; size_t left = 0; size_t len = 0; int r; params[0] = file_type; params[1] = 2; /* get the size */ 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; left = size = lebytes2ushort(count); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "got %"SC_FORMAT_LEN_SIZE_T"u bytes out_ptr=%p count&=%p count[0]=0x%02x count[1]=0x%02x, len=0x%04"SC_FORMAT_LEN_SIZE_T"x (%"SC_FORMAT_LEN_SIZE_T"u)", len, out_ptr, &count, count[0], count[1], size, size); out = out_ptr = malloc(size); if (out == NULL) { r = SC_ERROR_OUT_OF_MEMORY; goto fail; } for (offset += 2; left > 0; offset += len, left -= len, out_ptr += len) { len = MIN(left, CAC_MAX_CHUNK_SIZE); 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; fail: if (out) free(out); *out_len = 0; return 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 *tl = NULL, *val = NULL; 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; 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); if (priv->cache_buf) { free(priv->cache_buf); priv->cache_buf = NULL; priv->cache_buf_len = 0; } if (priv->object_type <= 0) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); 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; priv->cache_buf = malloc(tlv_len); if (priv->cache_buf == NULL) { r = SC_ERROR_OUT_OF_MEMORY; goto done; } priv->cache_buf_len = tlv_len; 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; 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); tlv_len -= tl_head_len; tl_len -= tl_head_len; /* 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 */ if (tlv_len < len) { len = tlv_len; } memcpy(tlv_ptr, val_ptr, len); } break; case CAC_OBJECT_TYPE_CERT: /* read file */ 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; 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; 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; } if (tag == CAC_TAG_CERTINFO) { if ((len >= 1) && (val_len >=1)) { cert_type = *val_ptr; } } 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 && 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); } else { sc_log(card->ctx, "Can't read zero-length certificate"); 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; } /* 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); memcpy(buf, &priv->cache_buf[idx], len); r = (int)len; done: if (tl) free(tl); if (val) free(val); LOG_FUNC_RETURN(card->ctx, r); } /* initialize getting a list and return the number of elements in the list */ static int cac_get_init_and_get_count(list_t *list, cac_object_t **entry, int *countp) { *countp = list_size(list); list_iterator_start(list); *entry = list_iterator_next(list); return SC_SUCCESS; } /* finalize the list iterator */ static int cac_final_iterator(list_t *list) { list_iterator_stop(list); return SC_SUCCESS; } /* fill in the obj_info for the current object on the list and advance to the next object */ static int cac_fill_object_info(list_t *list, cac_object_t **entry, sc_pkcs15_data_info_t *obj_info) { memset(obj_info, 0, sizeof(sc_pkcs15_data_info_t)); if (*entry == NULL) { return SC_ERROR_FILE_END_REACHED; } obj_info->path = (*entry)->path; obj_info->path.count = CAC_MAX_SIZE-1; /* read something from the object */ obj_info->id.value[0] = ((*entry)->fd >> 8) & 0xff; obj_info->id.value[1] = (*entry)->fd & 0xff; obj_info->id.len = 2; strncpy(obj_info->app_label, (*entry)->name, SC_PKCS15_MAX_LABEL_SIZE-1); *entry = list_iterator_next(list); return SC_SUCCESS; } static int cac_get_serial_nr_from_CUID(sc_card_t* card, sc_serial_number_t* serial) { cac_private_data_t * priv = CAC_DATA(card); LOG_FUNC_CALLED(card->ctx); if (card->serialnr.len) { *serial = card->serialnr; 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, serial->len); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } 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) { cac_private_data_t * priv = CAC_DATA(card); 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_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: return cac_get_init_and_get_count(&priv->general_list, &priv->general_current, (int *)ptr); case SC_CARDCTL_CAC_INIT_GET_CERT_OBJECTS: return cac_get_init_and_get_count(&priv->pki_list, &priv->pki_current, (int *)ptr); case SC_CARDCTL_CAC_GET_NEXT_GENERIC_OBJECT: return cac_fill_object_info(&priv->general_list, &priv->general_current, (sc_pkcs15_data_info_t *)ptr); case SC_CARDCTL_CAC_GET_NEXT_CERT_OBJECT: return cac_fill_object_info(&priv->pki_list, &priv->pki_current, (sc_pkcs15_data_info_t *)ptr); case SC_CARDCTL_CAC_FINAL_GET_GENERIC_OBJECTS: return cac_final_iterator(&priv->general_list); case SC_CARDCTL_CAC_FINAL_GET_CERT_OBJECTS: return cac_final_iterator(&priv->pki_list); } LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } static int cac_get_challenge(sc_card_t *card, u8 *rnd, size_t len) { /* CAC requires 8 byte response */ u8 rbuf[8]; u8 *rbufp = &rbuf[0]; size_t out_len = sizeof rbuf; int 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"); if (len < out_len) { out_len = len; } memcpy(rnd, rbuf, out_len); 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) { int r = SC_SUCCESS; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); 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); if (env->algorithm != SC_ALGORITHM_RSA) { r = SC_ERROR_NO_CARD_SUPPORT; } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } static int cac_restore_security_env(sc_card_t *card, int se_num) { SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } static int cac_rsa_op(sc_card_t *card, const u8 * data, size_t datalen, u8 * out, size_t outlen) { int r; u8 *outp, *rbuf; size_t rbuflen, outplen; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_log(card->ctx, "datalen=%"SC_FORMAT_LEN_SIZE_T"u outlen=%"SC_FORMAT_LEN_SIZE_T"u\n", datalen, outlen); outp = out; outplen = outlen; /* Not strictly necessary. This code requires the caller to have selected the correct PKI container * and authenticated to that container with the verifyPin command... All of this under the reader lock. * The PKCS #15 higher level driver code does all this correctly (it's the same for all cards, just * 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) LOG_FUNC_RETURN(card->ctx, r); rbuf = NULL; rbuflen = 0; for (; datalen > CAC_MAX_CHUNK_SIZE; data += CAC_MAX_CHUNK_SIZE, datalen -= CAC_MAX_CHUNK_SIZE) { r = cac_apdu_io(card, CAC_INS_SIGN_DECRYPT, CAC_P1_STEP, 0, data, CAC_MAX_CHUNK_SIZE, &rbuf, &rbuflen); if (r < 0) { break; } if (rbuflen != 0) { size_t n = MIN(rbuflen, outplen); memcpy(outp, rbuf, n); outp += n; outplen -= n; } free(rbuf); rbuf = NULL; rbuflen = 0; } if (r < 0) { goto err; } rbuf = NULL; rbuflen = 0; r = cac_apdu_io(card, CAC_INS_SIGN_DECRYPT, CAC_P1_FINAL, 0, data, datalen, &rbuf, &rbuflen); if (r < 0) { goto err; } if (rbuflen != 0) { size_t n = MIN(rbuflen, outplen); memcpy(outp, rbuf, n); /*outp += n; unused */ outplen -= n; } free(rbuf); rbuf = NULL; r = (int)(outlen - outplen); err: sc_unlock(card); if (r < 0) { sc_mem_clear(out, outlen); } if (rbuf) { free(rbuf); } LOG_FUNC_RETURN(card->ctx, r); } static int cac_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); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, cac_rsa_op(card, data, datalen, out, outlen)); } static int cac_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); 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 * 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 = CAC_DATA(card); assert(card != NULL && in_path != NULL); ctx = card->ctx; 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 */ priv->object_type = CAC_OBJECT_TYPE_GENERIC; if (cac_is_cert(priv, in_path)) { priv->object_type = CAC_OBJECT_TYPE_CERT; } /* 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; if (file_out != NULL) { apdu.p2 = 0; /* first record, return FCI */ } else { 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); } 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); /* 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; 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); 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 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); } /* 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, 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)); path->aid.len = sizeof(val->rid) + sizeof(val->applicationID); 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, "path->aid=%x %x %x %x %x %x %x len=%"SC_FORMAT_LEN_SIZE_T"u, path->value = %x %x len=%"SC_FORMAT_LEN_SIZE_T"u path->type=%d (%x)", path->aid.value[0], path->aid.value[1], path->aid.value[2], path->aid.value[3], path->aid.value[4], path->aid.value[5], path->aid.value[6], path->aid.len, path->value[0], path->value[1], path->len, path->type, path->type); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "rid=%x %x %x %x %x len=%"SC_FORMAT_LEN_SIZE_T"u appid= %x %x len=%"SC_FORMAT_LEN_SIZE_T"u objid= %x %x len=%"SC_FORMAT_LEN_SIZE_T"u", val->rid[0], val->rid[1], val->rid[2], val->rid[3], val->rid[4], sizeof(val->rid), val->applicationID[0], val->applicationID[1], sizeof(val->applicationID), val->objectID[0], val->objectID[1], sizeof(val->objectID)); return SC_SUCCESS; } 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; 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; unsigned short object_id; int r; r = cac_path_from_cardurl(card, &new_object.path, val, len); if (r != SC_SUCCESS) { return r; } switch (val->cardApplicationType) { case CAC_APP_TYPE_PKI: /* we don't want to overflow the cac_label array. This test could * go way if we create a label function that will create a unique label * from a cert index. */ if (priv->cert_next >= MAX_CAC_SLOTS) break; /* don't fail just because we have more certs than we can support */ 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); priv->cert_next++; break; case CAC_APP_TYPE_GENERAL: object_id = bebytes2ushort(val->objectID); obj = cac_find_obj_by_id(object_id); if (obj == NULL) break; /* don't fail just because we don't recognize the object */ new_object.name = obj->name; new_object.fd = 0; sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"CARDURL: gen_object found, objectID=%x (%s),", object_id, new_object.name); cac_add_object_to_list(&priv->general_list, &new_object); break; case CAC_APP_TYPE_SKI: sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"CARDURL: ski_object found"); break; default: 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; } return SC_SUCCESS; } static int cac_parse_cuid(sc_card_t *card, cac_private_data_t *priv, cac_cuid_t *val, size_t len) { size_t card_id_len; if (len < sizeof(cac_cuid_t)) { return SC_ERROR_INVALID_DATA; } sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "gsc_rid=%s", sc_dump_hex(val->gsc_rid, sizeof(val->gsc_rid))); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "manufacture id=%x", val->manufacturer_id); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "cac_type=%d", val->card_type); card_id_len = len - (&val->card_id - (u8 *)val); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "card_id=%s (%"SC_FORMAT_LEN_SIZE_T"u)", 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; } memcpy(priv->cac_id, &val->card_id, card_id_len); priv->cac_id_len = card_id_len; return SC_SUCCESS; } 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, const u8 *tl, size_t tl_len, u8 *val, size_t val_len, int depth) { size_t len = 0; const u8 *tl_end = tl + tl_len; const u8 *val_end = val + val_len; sc_path_t new_path; int r; for (; (tl < tl_end) && (val< val_end); val += len) { /* get the tag and the length */ u8 tag; 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"); r = cac_parse_cuid(card, priv, (cac_cuid_t *)val, len); if (r < 0) 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: Grammar Version = 0x%02x", *val); break; case CAC_TAG_CARDURL: sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"TAG:CARDURL"); r = cac_parse_cardurl(card, priv, (cac_card_url_t *)val, len); if (r < 0) return r; break; /* * The following are really for file systems cards. This code only cares about CAC VM cards */ case CAC_TAG_PKCS15: 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%02x)", tag); break; case CAC_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"); r = cac_path_from_cardurl(card, &new_path, (cac_card_url_t *)val, len); if (r < 0) return r; r = cac_select_file_by_type(card, &new_path, NULL); if (r < 0) return r; /* 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%02x)",tag ); break; } } return SC_SUCCESS; } 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 = 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) goto done; r = cac_read_file(card, CAC_FILE_VALUE, &val, &val_len); if (r < 0) goto done; r = cac_parse_CCC(card, priv, tl, tl_len, val, val_len, depth); done: if (tl) free(tl); if (val) free(val); return r; } /* 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_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) { /* 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 SC_SUCCESS; } } return SC_ERROR_OBJECT_NOT_FOUND; } /* * This emulates CCC for Alt tokens, that do not come with CCC nor ACA applets */ 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_cac_pki_obj; u8 buf[100]; u8 *val; /* 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); } } /* populate non-PKI objects */ 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, "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_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; 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; } 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; } 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 */ 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-2 specified in NIST Interagency Report 6887 - * "Government Smart Card Interoperability Specification v2.1 July 2003" */ r = cac_select_CCC(card); if (r == SC_SUCCESS) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "CCC found, is CAC-2"); if (!initialize) /* match card only */ return r; priv = cac_new_private_data(); if (!priv) return SC_ERROR_OUT_OF_MEMORY; r = cac_process_CCC(card, priv, 0); if (r == SC_SUCCESS) { card->type = SC_CARD_TYPE_CAC_II; card->drv_data = priv; return r; } } /* 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, "ACA found, is CAC-2 without CCC"); if (!initialize) /* match card only */ return r; if (!priv) { priv = cac_new_private_data(); if (!priv) return SC_ERROR_OUT_OF_MEMORY; } r = cac_process_ACA(card, priv); if (r == SC_SUCCESS) { 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); } 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 int cac_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left) { /* 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) { size_t i = 0; if (data->pin2.len < 6) { return SC_ERROR_INVALID_PIN_LENGTH; } for(i=0; i < data->pin2.len; ++i) { if (!isdigit(data->pin2.data[i])) { 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; } } rv = iso_drv->ops->pin_cmd(card, data, tries_left); data->apdu = NULL; return rv; } static struct sc_card_operations cac_ops; static struct sc_card_driver cac_drv = { "Common Access Card (CAC)", "cac", &cac_ops, NULL, 0, NULL }; static struct sc_card_driver * sc_get_driver(void) { struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); cac_ops = *iso_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.get_challenge = cac_get_challenge; cac_ops.read_binary = cac_read_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; } struct sc_card_driver * sc_get_cac_driver(void) { return sc_get_driver(); } OpenSC-0.26.1/src/libopensc/card-cac1.c000066400000000000000000000354371474147347300174100ustar00rootroot00000000000000/* * 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) { *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 = CAC_DATA(card); assert(card != NULL && in_path != NULL); ctx = card->ctx; 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(); } OpenSC-0.26.1/src/libopensc/card-cardos-common.c000066400000000000000000000051361474147347300213330ustar00rootroot00000000000000/* * 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)); } OpenSC-0.26.1/src/libopensc/card-cardos-common.h000066400000000000000000000032411474147347300213330ustar00rootroot00000000000000/* * 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 */ OpenSC-0.26.1/src/libopensc/card-cardos.c000066400000000000000000001310171474147347300200430ustar00rootroot00000000000000/* * card-cardos.c: Support for CardOS (from Siemens or Atos) based cards and * tokens (for example Aladdin eToken PRO, Eutron CryptoIdentity IT-SEC) * * Copyright (c) 2005 Nils Larsch * 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., 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 "asn1.h" #include "cardctl.h" static const struct sc_card_operations *iso_ops = NULL; static struct sc_card_operations cardos_ops; static struct sc_card_driver cardos_drv = { "Siemens CardOS", "cardos", &cardos_ops, NULL, 0, NULL }; 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 }, /* 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 */ { "3b:d2:18:02:c1:0a:31:fe:58:c8:0d:51", NULL, NULL, SC_CARD_TYPE_CARDOS_M4_4, 0, NULL}, /* 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: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 } }; /* 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] = {0}; int i; i = _sc_match_atr(card, cardos_atrs, &card->type); if (i < 0) return 0; memcpy(atr, card->atr.value, card->atr.len); /* Do not change card type for CIE! */ if (card->type == SC_CARD_TYPE_CARDOS_CIE_V1) return 1; if (card->type == SC_CARD_TYPE_CARDOS_M4_4) 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 = {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) || (atr[9] != 0x55 && atr[9] != 0x58)) return 0; /* get the os version using GET DATA and compare it with * version in the ATR */ 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); 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_log(card->ctx, "found cardos m4.01"); card->type = SC_CARD_TYPE_CARDOS_M4_01; } else if (atr[11] == 0x08) { sc_log(card->ctx, "found cardos v4.3b"); card->type = SC_CARD_TYPE_CARDOS_M4_3; } else if (atr[11] == 0x09) { sc_log(card->ctx, "found cardos v4.2b"); card->type = SC_CARD_TYPE_CARDOS_M4_2B; } else if (atr[11] >= 0x0B) { sc_log(card->ctx, "found cardos v4.2c or higher"); card->type = SC_CARD_TYPE_CARDOS_M4_2C; } else { sc_log(card->ctx, "found cardos m4.2"); } } return 1; } 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, *pp; size_t len, tlen = 0, ilen = 0; sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0x01, 0x88); apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.lc = 0; apdu.le = 256; r = sc_transmit_apdu(card, &apdu); 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) { pp = sc_asn1_find_tag(card->ctx, p, len, 0xe1, &tlen); if (pp == NULL) return 0; q = sc_asn1_find_tag(card->ctx, pp, tlen, 0x01, &ilen); if (q == NULL || ilen != 4) return 0; if (q[0] == 0x1c) return 1; p += tlen; len -= tlen + 2; } 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) { cardos_data_t * priv = NULL; unsigned long flags = 0; size_t data_field_length; sc_apdu_t apdu; u8 rbuf[2]; int r; 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; /* let user override flags and type from opensc.conf */ /* user can override card->type too.*/ if (card->flags) { flags = card->flags; } else { /* 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) { r = cardos_have_2048bit_package(card); if (r < 0) { r = SC_ERROR_INVALID_CARD; goto err; } if (r == 1) priv->rsa_2048 = 1; card->caps |= SC_CARD_CAP_APDU_EXT; } 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 || 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 */ sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0x01, 0x8D); apdu.le = sizeof rbuf; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); 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]); /* 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; 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; } /*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); } 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"}, { 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 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)"}, /* 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"}, /* (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 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 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_log(card->ctx, "%s\n", cardos_errors[i].errorstr); return cardos_errors[i].errorno; } } sc_log(card->ctx, "Unknown SWs; SW1=%02X, SW2=%02X\n", sw1, sw2); return SC_ERROR_CARD_CMD_FAILED; } 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, *q, *tag; int r; size_t fids = 0, len; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); /* 0x16: DIRECTORY */ /* 0x02: list both DF and EF */ get_next_part: sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x16, 0x02, offset); apdu.cla = 0x80; apdu.le = 256; apdu.resplen = 256; 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, "DIRECTORY command returned error"); if (apdu.resplen > 256) { 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 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, tag, tlen, 0x86, &ilen); if (q == NULL || ilen != 2) { sc_log(card->ctx, "error parsing file id TLV object"); return SC_ERROR_INTERNAL; } /* put file id in buf */ if (buflen >= 2) { buf[fids++] = q[0]; buf[fids++] = q[1]; buflen -= 2; } else /* not enough space left in buffer => break */ break; /* extract next offset */ q = sc_asn1_find_tag(card->ctx, tag, tlen, 0x8a, &ilen); if (q != NULL && ilen == 1) { offset = (u8)ilen; goto get_next_part; } } r = (int)fids; LOG_FUNC_RETURN(card->ctx, 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 */ SC_AC_OP_UPDATE, /* 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_DELETE, /* (delete) EF */ /* 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 (-> cyclic 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 cardos_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); LOG_FUNC_RETURN(card->ctx, r); } static int cardos_acl_to_bytes(sc_card_t *card, const sc_file_t *file, u8 *buf, size_t *outlen) { int i, byte; const int *idx; if (buf == NULL || *outlen < 9) return SC_ERROR_INVALID_ARGUMENTS; 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_log(card->ctx, "Invalid ACL\n"); return SC_ERROR_INVALID_ARGUMENTS; } buf[i] = byte; } *outlen = 9; return SC_SUCCESS; } static int cardos_set_file_attributes(sc_card_t *card, sc_file_t *file) { int r; if (file->type_attr_len == 0) { u8 type[3]; memset(type, 0, sizeof(type)); type[0] = 0x00; switch (file->type) { case SC_FILE_TYPE_WORKING_EF: break; case SC_FILE_TYPE_DF: type[0] = 0x38; break; default: return SC_ERROR_NOT_SUPPORTED; } 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: return SC_ERROR_NOT_SUPPORTED; /* No idea what this means, but it * seems to be required for key * generation. */ case SC_FILE_EF_LINEAR_VARIABLE_TLV: type[1] = 0xff; /* fall through */ default: type[0] |= file->ef_structure & 7; break; } } r = sc_file_set_type_attr(file, type, sizeof(type)); if (r != SC_SUCCESS) return r; } if (file->prop_attr_len == 0) { u8 status[3]; status[0] = 0x01; if (file->type == SC_FILE_TYPE_DF) { status[1] = (file->size >> 8) & 0xFF; status[2] = file->size & 0xFF; } else { status[1] = status[2] = 0x00; /* not used */ } r = sc_file_set_prop_attr(file, status, sizeof(status)); if (r != SC_SUCCESS) return r; } if (file->sec_attr_len == 0) { u8 acl[9]; size_t blen = sizeof(acl); r = cardos_acl_to_bytes(card, file, acl, &blen); if (r != SC_SUCCESS) return r; r = sc_file_set_sec_attr(file, acl, blen); if (r != SC_SUCCESS) return r; } return SC_SUCCESS; } /* newer versions of cardos seems to prefer the FCP */ static int cardos_construct_fcp(sc_card_t *card, const sc_file_t *file, u8 *out, size_t *outlen) { u8 buf[64], *p = out; size_t inlen = *outlen, len; int r; LOG_FUNC_CALLED(card->ctx); if (out == NULL || inlen < 64) return SC_ERROR_INVALID_ARGUMENTS; /* add FCP tag */ *p++ = 0x62; /* 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; if (file->type == SC_FILE_TYPE_DF) r = sc_asn1_put_tag(0x81, buf, 2, p, 4, &p); else r = sc_asn1_put_tag(0x80, buf, 2, p, 4, &p); if (r != SC_SUCCESS) return r; /* set file type */ if (file->shareable != 0) buf[0] = 0x40; else buf[0] = 0x00; if (file->type == SC_FILE_TYPE_WORKING_EF) { switch (file->ef_structure) { case SC_FILE_EF_TRANSPARENT: buf[0] |= 0x01; break; case SC_FILE_EF_LINEAR_VARIABLE_TLV: buf[0] |= 0x05; break; case SC_FILE_EF_LINEAR_FIXED: buf[0] |= 0x02; buf[1] |= 0x21; buf[2] |= 0x00; buf[3] |= (u8) file->record_length; buf[4] |= (u8) file->record_count; break; case SC_FILE_EF_CYCLIC: buf[0] |= 0x06; buf[1] |= 0x21; buf[2] |= 0x00; buf[3] |= (u8) file->record_length; buf[4] |= (u8) file->record_count; break; default: sc_log(card->ctx, "unknown EF type: %u", file->type); return SC_ERROR_INVALID_ARGUMENTS; } if (file->ef_structure == SC_FILE_EF_CYCLIC || file->ef_structure == SC_FILE_EF_LINEAR_FIXED) r = sc_asn1_put_tag(0x82, buf, 5, p, 8, &p); else r = sc_asn1_put_tag(0x82, buf, 1, p, 8, &p); } else if (file->type == SC_FILE_TYPE_DF) { buf[0] |= 0x38; r = sc_asn1_put_tag(0x82, buf, 1, p, 8, &p); } else return SC_ERROR_NOT_SUPPORTED; if (r != SC_SUCCESS) return r; /* set file id */ buf[0] = (file->id >> 8) & 0xff; buf[1] = file->id & 0xff; r = sc_asn1_put_tag(0x83, buf, 2, p, 8, &p); if (r != SC_SUCCESS) return r; /* set aid (for DF only) */ if (file->type == SC_FILE_TYPE_DF && file->namelen != 0) { r = sc_asn1_put_tag(0x84, file->name, file->namelen, p, 20, &p); if (r != SC_SUCCESS) return r; } /* set proprietary file attributes */ buf[0] = 0x00; /* use default values */ if (file->type == SC_FILE_TYPE_DF) r = sc_asn1_put_tag(0x85, buf, 1, p, 8, &p); else { buf[1] = 0x00; buf[2] = 0x00; r = sc_asn1_put_tag(0x85, buf, 1, p, 8, &p); } if (r != SC_SUCCESS) return r; /* set ACs */ len = 9; r = cardos_acl_to_bytes(card, file, buf, &len); if (r != SC_SUCCESS) return r; r = sc_asn1_put_tag(0x86, buf, len, p, 18, &p); if (r != SC_SUCCESS) return r; /* finally set the length of the FCP */ out[1] = p - out - 2; *outlen = p - out; return SC_SUCCESS; } static int cardos_create_file(sc_card_t *card, sc_file_t *file) { int r; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (card->type == SC_CARD_TYPE_CARDOS_GENERIC || card->type == SC_CARD_TYPE_CARDOS_M4_01) { r = cardos_set_file_attributes(card, file); if (r != SC_SUCCESS) return r; return iso_ops->create_file(card, file); } else if (card->type == SC_CARD_TYPE_CARDOS_M4_2 || 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) { u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; size_t len = sizeof(sbuf); sc_apdu_t apdu; r = cardos_construct_fcp(card, file, sbuf, &len); if (r < 0) { 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); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); } else return SC_ERROR_NOT_SUPPORTED; } /* * Restore the indicated SE */ static int cardos_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, 0, se_num); apdu.p1 = (card->type == SC_CARD_TYPE_CARDOS_CIE_V1 ? 0xF3 : 0x03); 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); } /* * 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 cardos_set_security_env(sc_card_t *card, const sc_security_env_t *env, int se_num) { cardos_data_t* priv = (cardos_data_t*)card->drv_data; sc_apdu_t apdu; u8 data[9]; int key_id, r; assert(card != NULL && env != NULL); 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); if (card->type == SC_CARD_TYPE_CARDOS_CIE_V1) { cardos_restore_security_env(card, 0x30); apdu.p1 = 0xF1; } else { apdu.p1 = 0x41; } 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; } 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); 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"); do { const struct sc_supported_algo_info* algorithm_info = env->supported_algos; int i=0; int algorithm_id_count = 0; for(i=0;ictx, "is signature"); sc_log(card->ctx, "Adding ID %d at index %d", algorithm_id, algorithm_id_count); 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); } priv -> algorithm_ids_in_tokeninfo_count = algorithm_id_count; } while (0); LOG_FUNC_RETURN(card->ctx, 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) { /* cardos_data_t* priv = (cardos_data_t*)card->drv_dataa */; int r; sc_apdu_t apdu; /* 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, 0x2A, 0x9E, 0x9A); apdu.resp = out; apdu.le = outlen; apdu.resplen = outlen; apdu.data = data; apdu.lc = datalen; apdu.datalen = datalen; 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) 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)); } 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; 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); ctx = card->ctx; priv = (cardos_data_t*)card->drv_data; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); /* 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 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. * The problem is, that that information is only available for the whole token and not for a specific key, so if both operations are present, we can only do trial and error * * The Algorithm IDs for RSA_SIG are 0x86 and 0x88, those for RSA_PURE_SIG 0x8c and 0x8a * (According to http://www.opensc-project.org/pipermail/opensc-devel/2010-September/014912.html * and www.crysys.hu/infsec/M40_Manual_E_2001_10.pdf) */ /* check the the algorithmIDs from the AlgorithmInfo */ for (i = 0; i < priv->algorithm_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; } } /* check if any operation was selected */ if (do_rsa_sig == 0 && do_rsa_pure_sig == 0) { /* no operation selected. we just have to try both, * for the lack of any better reasoning */ sc_log(ctx, "I was unable to determine, whether this key can be used with RSA_SIG or RSA_PURE_SIG. I will just try both."); do_rsa_sig = 1; do_rsa_pure_sig = 1; } if(do_rsa_pure_sig == 1){ sc_log(ctx, "trying RSA_PURE_SIG (padded DigestInfo)"); r = do_compute_signature(card, data, datalen, out, outlen); if (r >= SC_SUCCESS) LOG_FUNC_RETURN(ctx, r); } 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, stripped_data, &stripped_datalen); if (r != SC_SUCCESS) { /* no pkcs1 bt01 padding => let's try zero padding * This can only work if the data tbs doesn't have a * leading 0 byte. */ while (*stripped_data == 0 && stripped_datalen != 0) { ++stripped_data; --stripped_datalen; } } 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, stripped_data, stripped_datalen, stripped_data, &stripped_datalen); if (r != SC_SUCCESS) { free(buf); LOG_FUNC_RETURN(ctx, r); } 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) { 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, 0x01, 0x83); apdu.cla = 0x00; apdu.le = 256; apdu.resplen = sizeof(rbuf); 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, "Card returned error"); if (apdu.resplen < 1) { LOG_TEST_RET(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "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_log(card->ctx, "Unknown lifecycle byte %d", rbuf[0]); r = SC_ERROR_INTERNAL; } LOG_FUNC_RETURN(card->ctx, r); } static int cardos_lifecycle_set(sc_card_t *card, int *mode) { sc_apdu_t apdu; int r; int current; int target; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); target = *mode; r = cardos_lifecycle_get(card, ¤t); if (r != SC_SUCCESS) return r; if (current == target || current == SC_CARDCTRL_LIFECYCLE_OTHER) return SC_SUCCESS; sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x10, 0, 0); apdu.cla = 0x80; apdu.le = 0; apdu.resplen = 0; apdu.resp = NULL; 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); } static int cardos_put_data_oci(sc_card_t *card, struct sc_cardctl_cardos_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); 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); } static int cardos_put_data_seci(sc_card_t *card, struct sc_cardctl_cardos_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); 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"); return r; } static int cardos_generate_key(sc_card_t *card, struct sc_cardctl_cardos_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 = 0x00; apdu.data= data; apdu.datalen = apdu.lc = sizeof(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, "GENERATE_KEY failed"); return r; } static int cardos_get_serialnr(sc_card_t *card, sc_serial_number_t *serial) { 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) && (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; } /* copy and return serial number */ memcpy(serial, &card->serialnr, sizeof(*serial)); return SC_SUCCESS; } static int cardos_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) { switch (cmd) { case SC_CARDCTL_CARDOS_PUT_DATA_FCI: break; case SC_CARDCTL_CARDOS_PUT_DATA_OCI: return cardos_put_data_oci(card, (struct sc_cardctl_cardos_obj_info *) ptr); break; case SC_CARDCTL_CARDOS_PUT_DATA_SECI: return cardos_put_data_seci(card, (struct sc_cardctl_cardos_obj_info *) ptr); break; 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: return cardos_lifecycle_set(card, (int *) ptr); case SC_CARDCTL_GET_SERIALNR: return cardos_get_serialnr(card, (sc_serial_number_t *)ptr); } 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 cardos_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_left) { struct sc_context *ctx = card->ctx; int rv; LOG_FUNC_CALLED(card->ctx); data->flags |= SC_PIN_CMD_NEED_PADDING; data->pin_reference |= 0x80; sc_log(ctx, "PIN_CMD(cmd:%i, ref:%i)", data->cmd, data->pin_reference); sc_log(ctx, "PIN1(max:%"SC_FORMAT_LEN_SIZE_T"u, min:%"SC_FORMAT_LEN_SIZE_T"u)", data->pin1.max_length, data->pin1.min_length); sc_log(ctx, "PIN2(max:%"SC_FORMAT_LEN_SIZE_T"u, min:%"SC_FORMAT_LEN_SIZE_T"u)", data->pin2.max_length, data->pin2.min_length); /* 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; rv = iso_ops->pin_cmd(card, data, tries_left); LOG_FUNC_RETURN(ctx, rv); } static int cardos_logout(sc_card_t *card) { if (card->type == SC_CARD_TYPE_CARDOS_M4_01 || card->type == SC_CARD_TYPE_CARDOS_M4_2 || card->type == SC_CARD_TYPE_CARDOS_M4_2B || 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_3) { sc_apdu_t apdu; int r; sc_path_t path; sc_format_path("3F00", &path); r = sc_select_file(card, &path, NULL); if (r != SC_SUCCESS) return r; sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0xEA, 0x00, 0x00); apdu.cla = 0x80; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); } else return SC_ERROR_NOT_SUPPORTED; } /* eToken R2 supports WRITE_BINARY, PRO Tokens support UPDATE_BINARY */ static struct sc_card_driver * sc_get_driver(void) { if (iso_ops == NULL) iso_ops = sc_get_iso7816_driver()->ops; 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; cardos_ops.card_ctl = cardos_card_ctl; cardos_ops.pin_cmd = cardos_pin_cmd; cardos_ops.logout = cardos_logout; return &cardos_drv; } #if 1 struct sc_card_driver * sc_get_cardos_driver(void) { return sc_get_driver(); } #endif OpenSC-0.26.1/src/libopensc/card-coolkey.c000066400000000000000000002175221474147347300202430ustar00rootroot00000000000000/* * card-coolkey.c: Support for Coolkey * * 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, Red Hat, Inc. * * Coolkey driver author: Robert Relyea * * 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 #include #include "internal.h" #include "asn1.h" #include "cardctl.h" #ifdef ENABLE_ZLIB #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' */ /* * COOLKEY hardware and APDU constants */ #define COOLKEY_MAX_CHUNK_SIZE 240 /* must be less than 255-8 */ /* ISO 7816 CLA values used by COOLKEY */ #define ISO7816_CLASS 0x00 #define COOLKEY_CLASS 0xb0 /* ISO 71816 INS values used by COOLKEY */ #define ISO7816_INS_SELECT_FILE 0xa4 /* COOLKEY specific INS values (public) */ #define COOLKEY_INS_GET_LIFE_CYCLE 0xf2 #define COOLKEY_INS_GET_STATUS 0x3c #define COOLKEY_INS_VERIFY_PIN 0x42 #define COOLKEY_INS_LIST_OBJECTS 0x58 /* COOLKEY specific INS values (require nonce) */ #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 0x72 #define COOLKEY_INS_READ_OBJECT 0x56 #define COOLKEY_INS_WRITE_OBJECT 0x54 #define COOLKEY_INS_LOGOUT 0x61 /* COMPUTE_CRYPT and COMPUT_ECC parameters */ #define COOLKEY_CRYPT_INIT 1 #define COOLKEY_CRYPT_PROCESS 2 #define COOLKEY_CRYPT_FINAL 3 #define COOLKEY_CRYPT_ONE_STEP 4 #define COOLKEY_CRYPT_MODE_RSA_NO_PAD 0x00 #define COOLKEY_CRYPT_LOCATION_APDU 0x01 #define COOLKEY_CRYPT_LOCATION_DL_OBJECT 0x02 #define COOLKEY_CRYPT_DIRECTION_ENCRYPT 0x03 /* List Objects parameters */ #define COOLKEY_LIST_RESET 0x00 #define COOLKEY_LIST_NEXT 0x01 /* Special object identifiers */ #define COOLKEY_DL_OBJECT_ID 0xffffffff #define COOLKEY_COMBINED_OBJECT_ID 0x7a300000 /* 'z0\0\0' */ #define COOLKEY_INVALID_KEY 0xff00 #define COOLKEY_KEY_CLASS 'k' #define COOLKEY_NONCE_SIZE 8 /* returned from the coolkey extended life cycle apdu */ typedef struct coolkey_life_cycle { u8 life_cycle; u8 pin_count; u8 protocol_version_major; u8 protocol_version_minor; } coolkey_life_cycle_t; /* return by the coolkey status apdu */ typedef struct coolkey_status { u8 protocol_version_major; u8 protocol_version_minor; u8 applet_major_version; u8 applet_minor_version; u8 total_object_memory[4]; u8 free_object_memory[4]; u8 pin_count; u8 key_count; u8 logged_in_identities[2]; } coolkey_status_t; /* 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]; u8 ic_batch[2]; u8 ic_serial_number[4]; } coolkey_cuid_t; /* parameter for list objects apdu */ typedef struct coolkey_object_info { u8 object_id[4]; u8 object_length[4]; u8 read_acl[2]; u8 write_acl[2]; u8 delete_acl[2]; } coolkey_object_info_t; /* parameter for the read object apdu */ typedef struct coolkey_read_object_param { u8 object_id[4]; u8 offset[4]; u8 length; } coolkey_read_object_param_t; /* parameter for the write object apdu */ typedef struct coolkey_write_object_param { coolkey_read_object_param_t head; u8 buf[COOLKEY_MAX_CHUNK_SIZE]; } coolkey_write_object_param_t; /* coolkey uses muscle like objects, but when coolkey is managed by the TPS system * it creates a single object and encodes the individual objects inside the * common single object. This allows more efficient reading of all the objects * (because we can use a single apdu call and we can compress all the objects * together and take advantage of the fact that many of the certs share the same subject and issue). */ typedef struct coolkey_combined_header { u8 format_version[2]; u8 object_version[2]; coolkey_cuid_t cuid; u8 compression_type[2]; u8 compression_length[2]; u8 compression_offset[2]; } coolkey_combined_header_t; #define COOLKEY_COMPRESSION_NONE 0 #define COOLKEY_COMPRESSION_ZLIB 1 /* * This is the header of the decompressed portion of the combined object */ typedef struct coolkey_decompressed_header { u8 object_offset[2]; u8 object_count[2]; u8 token_name_length; 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. * v1 is the most common, and is always found in a combined object, so * we only specify the v0 in the name of the structure. */ typedef struct coolkey_v0_object_header { u8 record_type; /* version 0 or version 1 */ u8 object_id[4]; /* coolkey object id */ u8 attribute_data_len[2]; /* the length in bytes of the next block of * attribute records */ /* followed by the first attribute record */ } coolkey_v0_object_header_t; typedef struct coolkey_v0_attribute_header { u8 attribute_attr_type[4]; /* CKA_ATTRIBUTE_TYPE */ u8 attribute_data_len[2]; /* Length of the attribute */ /* followed by the actual attribute data */ } coolkey_v0_attribute_header_t; /* combined objects are v1 objects without the record_type indicator */ typedef struct coolkey_combined_object_header { u8 object_id[4]; /* coolkey object id */ u8 fixed_attributes_values[4]; /* compressed fixed attributes */ u8 attribute_count[2]; /* the number of attribute records that follow */ /* followed by the first attribute */ } coolkey_combined_object_header_t; typedef struct coolkey_object_header { u8 record_type; /* version 0 or version 1 */ u8 object_id[4]; /* coolkey object id */ u8 fixed_attributes_values[4]; /* compressed fixed attributes */ u8 attribute_count[2]; /* the number of attribute records that follow */ /* followed by the first attribute */ } coolkey_object_header_t; #define COOLKEY_V0_OBJECT 0 #define COOLKEY_V1_OBJECT 1 /* vi attribute header */ typedef struct coolkey_attribute_header { u8 attribute_attr_type[4]; /* CKA_ATTRIBUTE_TYPE */ u8 attribute_data_type; /* the Type of data stored */ /* 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 #define COOLKEY_ATTR_TYPE_BOOL_FALSE 2 #define COOLKEY_ATTR_TYPE_BOOL_TRUE 3 /* * format of the fix_attribute values. These are stored as a big endian uint32_t with the below bit field * 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 reserved:8; }; * cka_class is used to determine which booleans are valid. Any attributes 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: * * 0 CKO_DATA cka_private, cka_modifiable, cka_token * 1 CKO_CERTIFICATE cka_private, cka_modifiable, cka_token * 2 CKO_PUBLIC_KEY cka_private, cka_modifiable, cka_token * cka_derive, cka_local, cka_encrypt, cka_wrap * cka_verify, cka_verify_recover * 3 CKO_PRIVATE_KEY cka_private, cka_modifiable, cka_token * cka_derive, cka_local, cka_decrypt, cka_unwrap * cka_sign, cka_sign_recover, cka_sensitive, * cka_always_sensitive, cka_extractable, * cka_never_extractable * 4 CKO_SECRET_KEY cka_private, cka_modifiable, cka_token * cka_derive, cka_local, cka_encrypt, cka_decrypt, * cka_wrap, cka_unwrap, cka_sign, cka_verify, * cka_sensitive, cka_always_sensitive, * cka_extractable, cka_never_extractable * 5-7 RESERVED none * */ /* * Coolkey attribute record handling functions. */ /* get the length of the attribute from a V1 attribute header. If encoded_len == true, then return the length of * the attribute data field (including any explicit length values, If encoded_len = false return the length of * the actual attribute data. */ static int coolkey_v1_get_attribute_len(const u8 *attr, size_t buf_len, size_t *len, int encoded_len) { coolkey_attribute_header_t *attribute_head = (coolkey_attribute_header_t *)attr; *len = 0; /* don't reference beyond our buffer */ if (buf_len < sizeof(coolkey_attribute_header_t)) { return SC_ERROR_CORRUPTED_DATA; } switch (attribute_head->attribute_data_type) { case COOLKEY_ATTR_TYPE_STRING: if (buf_len < (sizeof(coolkey_attribute_header_t) +2)) { break; } *len = bebytes2ushort(attr + sizeof(coolkey_attribute_header_t)); if (encoded_len) { *len += 2; } return SC_SUCCESS; case COOLKEY_ATTR_TYPE_BOOL_FALSE: case COOLKEY_ATTR_TYPE_BOOL_TRUE: /* NOTE: there is no encoded data from TYPE_BOOL_XXX, so we return length 0, but the length * of the attribute is actually 1 byte, so if encoded_len == false, return 1 */ *len = encoded_len ? 0: 1; return SC_SUCCESS; break; case COOLKEY_ATTR_TYPE_INTEGER: *len = 4; /* length is 4 in both encoded length and attribute length */ return SC_SUCCESS; default: break; } return SC_ERROR_CORRUPTED_DATA; } /* length of the attribute data is stored in the header of the v0 record */ static int coolkey_v0_get_attribute_len(const u8 *attr, size_t buf_len, size_t *len) { coolkey_v0_attribute_header_t *attribute_head = (coolkey_v0_attribute_header_t *)attr; /* don't reference beyond our buffer */ if (buf_len < sizeof(coolkey_v0_attribute_header_t)) { return SC_ERROR_CORRUPTED_DATA; } *len = bebytes2ushort(attribute_head->attribute_data_len); return SC_SUCCESS; } /* these next 3 functions gets the length of the full attribute record, including * the attribute header */ static size_t coolkey_v1_get_attribute_record_len(const u8 *attr, size_t buf_len) { size_t attribute_len = sizeof(coolkey_attribute_header_t); size_t len = 0; int r; r = coolkey_v1_get_attribute_len(attr, buf_len, &len, 1); if (r < 0) { return buf_len; /* skip to the end, ignore the rest of the record */ } return MIN(buf_len,attribute_len+len); } static size_t coolkey_v0_get_attribute_record_len(const u8 *attr, size_t buf_len) { size_t attribute_len = sizeof(coolkey_v0_attribute_header_t); size_t len; int r; r = coolkey_v0_get_attribute_len(attr, buf_len, &len); if (r < 0) { return buf_len; /* skip to the end, ignore the rest of the record */ } return MIN(buf_len,attribute_len+len); } static size_t coolkey_get_attribute_record_len(const u8 *attr, u8 obj_record_type, size_t buf_len) { if (obj_record_type == COOLKEY_V0_OBJECT) { return coolkey_v0_get_attribute_record_len(attr, buf_len); } if (obj_record_type != COOLKEY_V1_OBJECT) { return buf_len; /* skip to the end */ } return coolkey_v1_get_attribute_record_len(attr, buf_len); } /* * Attribute type shows up in the same place in all attribute record types. Carry record_type in case * this changes in the future. */ static CK_ATTRIBUTE_TYPE coolkey_get_attribute_type(const u8 *attr, u8 obj_record_type, size_t buf_len) { coolkey_attribute_header_t *attribute_header = (coolkey_attribute_header_t *) attr; return bebytes2ulong(attribute_header->attribute_attr_type); } /* * return the start of the attribute section based on the record type */ static const u8 * coolkey_attribute_start(const u8 *obj, u8 object_record_type, size_t buf_len) { size_t offset = object_record_type == COOLKEY_V1_OBJECT ? sizeof(coolkey_object_header_t) : sizeof(coolkey_v0_object_header_t); if ((object_record_type != COOLKEY_V1_OBJECT) && (object_record_type != COOLKEY_V0_OBJECT)) { return NULL; } if (offset > buf_len) { return NULL; } return obj + offset; } /* * We don't have the count in the header for v0 attributes, * Count them. */ static int coolkey_v0_get_attribute_count(const u8 *obj, size_t buf_len) { coolkey_v0_object_header_t *object_head = (coolkey_v0_object_header_t *)obj; const u8 *attr; int count = 0; size_t attribute_data_len; /* make sure we have enough of the object to read the record_type */ if (buf_len <= sizeof(coolkey_v0_object_header_t)) { return 0; } /* * now loop through all the attributes in the list. first find the start of the list */ attr = coolkey_attribute_start(obj, COOLKEY_V0_OBJECT, buf_len); if (attr == NULL) { return 0; } buf_len -= (attr-obj); attribute_data_len = bebytes2ushort(object_head->attribute_data_len); if (buf_len < attribute_data_len) { return 0; } while (attribute_data_len) { size_t len = coolkey_v0_get_attribute_record_len(attr, buf_len); if (len == 0) { break; } /* This is an error in the token data, don't parse the last attribute */ if (len > attribute_data_len) { break; } /* we know that coolkey_v0_get_attribute_record_len never * returns more than buf_len, so we can safely assert that. * 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); count++; attr += len; buf_len -= len; attribute_data_len -= len; } return count; } static int coolkey_v1_get_attribute_count(const u8 *obj, size_t buf_len) { coolkey_object_header_t *object_head = (coolkey_object_header_t *)obj; if (buf_len <= sizeof(coolkey_object_header_t)) { return 0; } return bebytes2ushort(object_head->attribute_count); } static int coolkey_get_attribute_count(const u8 *obj, u8 object_record_type, size_t buf_len) { if (object_record_type == COOLKEY_V0_OBJECT) { return coolkey_v0_get_attribute_count(obj, buf_len); } if (object_record_type != COOLKEY_V1_OBJECT) { return 0; } return coolkey_v1_get_attribute_count(obj, buf_len); } /* * The next three functions return a parsed attribute value from an attribute record. */ static int coolkey_v0_get_attribute_data(const u8 *attr, size_t buf_len, sc_cardctl_coolkey_attribute_t *attr_out) { /* we need to manually detect types CK_ULONG */ CK_ATTRIBUTE_TYPE attr_type = coolkey_get_attribute_type(attr, COOLKEY_V0_OBJECT, buf_len); int r; size_t len; attr_out->attribute_data_type = SC_CARDCTL_COOLKEY_ATTR_TYPE_STRING; attr_out->attribute_length = 0; attr_out->attribute_value = NULL; r = coolkey_v0_get_attribute_len(attr, buf_len, &len); 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) { return SC_ERROR_CORRUPTED_DATA; } attr_out->attribute_data_type = SC_CARDCTL_COOLKEY_ATTR_TYPE_ULONG; } /* return the length and the data */ attr_out->attribute_length = len; attr_out->attribute_value = attr + sizeof(coolkey_v0_attribute_header_t); return SC_SUCCESS; } static u8 coolkey_static_false = CK_FALSE; static u8 coolkey_static_true = CK_TRUE; static int coolkey_v1_get_attribute_data(const u8 *attr, size_t buf_len, sc_cardctl_coolkey_attribute_t *attr_out) { int r; size_t len; coolkey_attribute_header_t *attribute_head = (coolkey_attribute_header_t *)attr; if (buf_len < sizeof(coolkey_attribute_header_t)) { return SC_ERROR_CORRUPTED_DATA; } /* we must have type V1. Process according to data type */ switch (attribute_head->attribute_data_type) { /* ULONG has implied length of 4 */ case COOLKEY_ATTR_TYPE_INTEGER: if (buf_len < (sizeof(coolkey_attribute_header_t) + 4)) { return SC_ERROR_CORRUPTED_DATA; } attr_out->attribute_data_type = SC_CARDCTL_COOLKEY_ATTR_TYPE_ULONG; attr_out->attribute_length = 4; attr_out->attribute_value = attr + sizeof(coolkey_attribute_header_t); return SC_SUCCESS; /* BOOL_FALSE and BOOL_TRUE have implied length and data */ /* return type STRING for BOOLS */ case COOLKEY_ATTR_TYPE_BOOL_FALSE: attr_out->attribute_length = 1; attr_out->attribute_value = &coolkey_static_false; return SC_SUCCESS; case COOLKEY_ATTR_TYPE_BOOL_TRUE: attr_out->attribute_length = 1; attr_out->attribute_value = &coolkey_static_true; return SC_SUCCESS; /* string type has encoded length */ case COOLKEY_ATTR_TYPE_STRING: r = coolkey_v1_get_attribute_len(attr, buf_len, &len, 0); if (r < SC_SUCCESS) { return r; } if (buf_len < (len + sizeof(coolkey_attribute_header_t) + 2)) { return SC_ERROR_CORRUPTED_DATA; } attr_out->attribute_value = attr+sizeof(coolkey_attribute_header_t)+2; attr_out->attribute_length = len; return SC_SUCCESS; default: break; } return SC_ERROR_CORRUPTED_DATA; } int coolkey_get_attribute_data(const u8 *attr, u8 object_record_type, size_t buf_len, sc_cardctl_coolkey_attribute_t *attr_out) { /* handle the V0 objects first */ if (object_record_type == COOLKEY_V0_OBJECT) { return coolkey_v0_get_attribute_data(attr, buf_len, attr_out); } /* don't crash if we encounter some new or corrupted coolkey device */ if (object_record_type != COOLKEY_V1_OBJECT) { return SC_ERROR_NO_CARD_SUPPORT; } return coolkey_v1_get_attribute_data(attr, buf_len, attr_out); } /* convert an attribute type into a bit in the fixed attribute uint32_t */ static unsigned long coolkey_get_fixed_boolean_bit(CK_ATTRIBUTE_TYPE type) { switch(type) { case CKA_TOKEN: return 0x00000080; case CKA_PRIVATE: return 0x00000100; case CKA_MODIFIABLE: return 0x00000200; case CKA_DERIVE: return 0x00000400; case CKA_LOCAL: return 0x00000800; case CKA_ENCRYPT: return 0x00001000; case CKA_DECRYPT: return 0x00002000; case CKA_WRAP: return 0x00004000; case CKA_UNWRAP: return 0x00008000; case CKA_SIGN: return 0x00010000; case CKA_SIGN_RECOVER: return 0x00020000; case CKA_VERIFY: return 0x00040000; case CKA_VERIFY_RECOVER: return 0x00080000; case CKA_SENSITIVE: return 0x00100000; case CKA_ALWAYS_SENSITIVE: return 0x00200000; case CKA_EXTRACTABLE: return 0x00400000; case CKA_NEVER_EXTRACTABLE: return 0x00800000; default: break; } return 0; /* return no bits */ } /* This table lets us return a pointer to the CKA_ID value without allocating data or * 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, }; /* This table provides the following: * 1) a mapping from a 3 bit cka_class to a full 32 bit CKA_CLASS_TYPE value we can return. * 2) the mask of valid boolean attributes in the fixed attributes. */ struct coolkey_fixed_class { u8 class_value[4]; unsigned long boolean_mask; }; static const struct coolkey_fixed_class coolkey_static_cka_class[8] = { { { 0, 0, 0, 0}, 0x00000380 }, /* DATA */ { { 0, 0, 0, 1}, 0x00000380 }, /* CERTIFICATE */ { { 0, 0, 0, 2}, 0x000c5f80 }, /* PUBLIC_KEY */ { { 0, 0, 0, 3}, 0x00f3af80 }, /* PRIVATE_KEY */ { { 0, 0, 0, 4}, 0x00f5ff80 }, /* SECRET_KEY */ { { 0, 0, 0, 5}, 0x00000000 }, { { 0, 0, 0, 6}, 0x00000000 }, { { 0, 0, 0, 7}, 0x00000000 } }; /* * handle fixed attributes (V1 only) */ static int coolkey_get_attribute_data_fixed(CK_ATTRIBUTE_TYPE attr_type, unsigned long fixed_attributes, sc_cardctl_coolkey_attribute_t *attr_out) { unsigned long cka_id = fixed_attributes & 0xf; unsigned long cka_class = ((fixed_attributes) >> 4) & 0x7; unsigned long mask, bit; if (attr_type == CKA_ID) { attr_out->attribute_length = 1; attr_out->attribute_value= &coolkey_static_cka_id[cka_id]; return SC_SUCCESS; } if (attr_type == CKA_CLASS) { attr_out->attribute_data_type = SC_CARDCTL_COOLKEY_ATTR_TYPE_ULONG; attr_out->attribute_length = 4; attr_out->attribute_value = coolkey_static_cka_class[cka_class].class_value; return SC_SUCCESS; } /* If it matched, it must be one of the booleans */ mask = coolkey_static_cka_class[cka_class].boolean_mask; bit = coolkey_get_fixed_boolean_bit(attr_type); /* attribute isn't in the list */ if ((bit & mask) == 0) { return SC_ERROR_DATA_OBJECT_NOT_FOUND; } attr_out->attribute_length = 1; attr_out->attribute_value = bit & fixed_attributes ? &coolkey_static_true : &coolkey_static_false; return SC_SUCCESS; } static int coolkey_v1_get_object_length(u8 *obj, size_t buf_len) { coolkey_combined_object_header_t *object_head = (coolkey_combined_object_header_t *) obj; int attribute_count; u8 *current_attribute; int j; size_t len; len = sizeof(coolkey_combined_object_header_t); if (buf_len <= len) { return (int)buf_len; } attribute_count = bebytes2ushort(object_head->attribute_count); buf_len -= len; for (current_attribute = obj + len, j = 0; j < attribute_count; j++) { size_t attribute_len = coolkey_v1_get_attribute_record_len(current_attribute, buf_len); len += attribute_len; current_attribute += attribute_len; buf_len -= attribute_len; } return (int)len; } /* * COOLKEY private data per card state */ typedef struct coolkey_private_data { u8 protocol_version_major; u8 protocol_version_minor; u8 format_version_major; u8 format_version_minor; unsigned short object_version; u8 life_cycle; u8 pin_count; u8 *token_name; /* our token name read from the token */ size_t token_name_length; /* length of our token name */ u8 nonce[COOLKEY_NONCE_SIZE]; /* nonce returned from login */ int nonce_valid; coolkey_cuid_t cuid; /* card unique ID from the CCC */ 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 */ unsigned long algorithm; /* saved from set_security_env */ int operation; /* saved from set_security_env */ } coolkey_private_data_t; #define COOLKEY_DATA(card) ((coolkey_private_data_t*)card->drv_data) int 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; } /* For SimCList autocopy, we need to know the size of the data elements */ 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; 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; } static void coolkey_free_private_data(coolkey_private_data_t *priv) { list_t *l = &priv->objects_list; sc_cardctl_coolkey_object_t *o; /* Clean up the allocated memory in the items */ list_iterator_start(l); while (list_iterator_hasnext(l)) { o = (sc_cardctl_coolkey_object_t *)list_iterator_next(l); free(o->data); o->data = NULL; } list_iterator_stop(l); list_destroy(&priv->objects_list); free(priv->token_name); free(priv); return; } /* * Object list operations */ static int coolkey_add_object_to_list(list_t *list, const sc_cardctl_coolkey_object_t *object) { if (list_append(list, object) < 0) return SC_ERROR_UNKNOWN; return SC_SUCCESS; } #define COOLKEY_AID "\xA0\x00\x00\x01\x16" static sc_cardctl_coolkey_object_t * coolkey_find_object_by_id(list_t *list, unsigned long object_id) { int pos; static sc_cardctl_coolkey_object_t cmp = {{ "", 0, 0, 0, SC_PATH_TYPE_DF_NAME, { COOLKEY_AID, sizeof(COOLKEY_AID)-1 } }, 0, 0, NULL}; cmp.id = object_id; if ((pos = list_locate(list, &cmp)) < 0) return NULL; return list_get_at(list, pos); } static const sc_path_t coolkey_template_path = { "", 0, 0, 0, SC_PATH_TYPE_DF_NAME, { COOLKEY_AID, sizeof(COOLKEY_AID)-1 } }; struct coolkey_error_codes_st { int sc_error; char *description; }; static const struct coolkey_error_codes_st coolkey_error_codes[]= { {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, "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, "Reserved 0x9c0a" }, {SC_ERROR_SM_INVALID_CHECKSUM, "Signature invalid" }, {SC_ERROR_AUTH_METHOD_BLOCKED, "Identity blocked" }, {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" }, {SC_ERROR_FILE_END_REACHED, "Sequence End" }, }; static const unsigned int coolkey_number_of_error_codes = sizeof(coolkey_error_codes)/sizeof(coolkey_error_codes[0]); static int coolkey_check_sw(sc_card_t *card, unsigned int sw1, unsigned int sw2) { sc_log(card->ctx, "sw1 = 0x%02x, sw2 = 0x%02x\n", sw1, sw2); if (sw1 == 0x90 && sw2 == 0x00) return SC_SUCCESS; if (sw1 == 0x9c) { if (sw2 == 0xff) { /* shouldn't happen on a production applet, 0x9cff is a debugging error code */ return SC_ERROR_INTERNAL; } if (sw2 >= coolkey_number_of_error_codes) { return SC_ERROR_UNKNOWN; } return coolkey_error_codes[sw2].sc_error; } /* iso error */ return sc_get_iso7816_driver()->ops->check_sw(card, sw1, sw2); } /* * Send a command and receive data. * * 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. * * modelled after a similar function in card-piv.c. The coolkey version * adds the coolkey nonce to user authenticated operations. */ static int coolkey_apdu_io(sc_card_t *card, int cla, int ins, int p1, int p2, const u8 * sendbuf, size_t sendbuflen, u8 ** recvbuf, size_t * recvbuflen, const u8 *nonce, size_t nonce_len) { int r; sc_apdu_t apdu; u8 rbufinitbuf[COOLKEY_MAX_SIZE]; u8 rsendbuf[COOLKEY_MAX_SIZE]; u8 *rbuf; size_t rbuflen; int cse = 0; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); 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); rbuf = rbufinitbuf; rbuflen = sizeof(rbufinitbuf); /* if caller provided a buffer and length */ if (recvbuf && *recvbuf && recvbuflen && *recvbuflen) { rbuf = *recvbuf; rbuflen = *recvbuflen; } if (sendbuf || nonce) { if (recvbuf) { cse = SC_APDU_CASE_4_SHORT; } else { cse = SC_APDU_CASE_3_SHORT; } } else { if (recvbuf) { cse = SC_APDU_CASE_2_SHORT; } else { cse = SC_APDU_CASE_1; } } /* append the nonce if we have it. Coolkey just blindly puts this at the end * of the APDU (while adjusting lc). This converts case 1 to case 3. coolkey * also always drops le in case 4 (which happens when proto = T0). nonces are * never used on case 2 commands, so we can simply append the nonce to the data * and we should be fine */ if (nonce) { u8 *buf = rsendbuf; if (sendbuf) { sendbuflen = MIN(sendbuflen,sizeof(rsendbuf)-nonce_len); memcpy(rsendbuf, sendbuf, sendbuflen); buf += sendbuflen; } memcpy(buf, nonce, nonce_len); sendbuflen += nonce_len; sendbuf =rsendbuf; } sc_format_apdu(card, &apdu, cse, ins, p1, p2); apdu.lc = sendbuflen; apdu.datalen = sendbuflen; apdu.data = sendbuf; /* coolkey uses non-standard classes */ apdu.cla = cla; if (recvbuf) { apdu.resp = rbuf; apdu.le = (rbuflen > 255) ? 255 : rbuflen; apdu.resplen = rbuflen; } else { apdu.resp = rbuf; apdu.le = 0; apdu.resplen = 0; } 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_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_log(card->ctx, "Transmit failed"); goto err; } r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r < 0) { sc_log(card->ctx, "Transmit failed"); goto err; } if (recvbuflen) { if (recvbuf && *recvbuf == NULL) { *recvbuf = malloc(apdu.resplen); if (*recvbuf == NULL) { r = SC_ERROR_OUT_OF_MEMORY; goto err; } memcpy(*recvbuf, rbuf, apdu.resplen); } *recvbuflen = apdu.resplen; r = (int)*recvbuflen; } err: LOG_FUNC_RETURN(card->ctx, r); } /* * Helpers to handle coolkey commands */ static int coolkey_get_life_cycle(sc_card_t *card, coolkey_life_cycle_t *life_cycle) { coolkey_status_t status; u8 *receive_buf; size_t receive_len; int len; receive_len = sizeof(*life_cycle); receive_buf = (u8 *)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; } receive_len = 1; receive_buf = &life_cycle->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 < 0) { /* Error from the trasmittion */ return len; } if (len != 1) { /* The returned data is invalid */ return SC_ERROR_INTERNAL; } receive_len = sizeof(status); receive_buf = (u8 *)&status; 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; life_cycle->pin_count = status.pin_count; return SC_SUCCESS; } /* select the coolkey applet */ static int coolkey_select_applet(sc_card_t *card) { u8 aid[] = { 0x62, 0x76, 0x01, 0xff, 0x00, 0x00, 0x00 }; return coolkey_apdu_io(card, ISO7816_CLASS, ISO7816_INS_SELECT_FILE, 4, 0, &aid[0], sizeof(aid), NULL, NULL, NULL, 0); } static void coolkey_make_cuid_from_cplc(coolkey_cuid_t *cuid, global_platform_cplc_data_t *cplc_data) { cuid->ic_fabricator[0] = cplc_data->ic_fabricator[0]; cuid->ic_fabricator[1] = cplc_data->ic_fabricator[1]; cuid->ic_type[0] = cplc_data->ic_type[0]; cuid->ic_type[1] = cplc_data->ic_type[1]; cuid->ic_batch[0] = cplc_data->ic_batch[0]; cuid->ic_batch[1] = cplc_data->ic_batch[1]; cuid->ic_serial_number[0] = cplc_data->ic_serial_number[0]; cuid->ic_serial_number[1] = cplc_data->ic_serial_number[1]; cuid->ic_serial_number[2] = cplc_data->ic_serial_number[2]; cuid->ic_serial_number[3] = cplc_data->ic_serial_number[3]; } /* * Read a COOLKEY coolkey object. */ static int coolkey_read_object(sc_card_t *card, unsigned long object_id, size_t offset, u8 *out_buf, size_t out_len, u8 *nonce, size_t nonce_size) { coolkey_read_object_param_t params; u8 *out_ptr; size_t left = 0; 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; left = out_len; do { ulong2bebytes(¶ms.offset[0], offset); params.length = MIN(left, COOLKEY_MAX_CHUNK_SIZE); 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; } /* sanity check to make sure we don't overflow left */ if ((left < len) || (len == 0)) { r = SC_ERROR_INTERNAL; goto fail; } out_ptr += len; offset += len; left -= len; } while (left != 0); return (int)out_len; fail: LOG_FUNC_RETURN(card->ctx, r); } /* * Write a COOLKEY coolkey object. */ static int coolkey_write_object(sc_card_t *card, unsigned long object_id, size_t offset, const u8 *buf, size_t buf_len, const u8 *nonce, size_t nonce_size) { coolkey_write_object_param_t params; 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, 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, (u8 *)¶ms, sizeof(params.head)+operation_len, NULL, 0, nonce, nonce_size); if (r < 0) { goto fail; } buf += operation_len; offset += operation_len; left -= operation_len; } while (left != 0); return (int)(buf_len - left); fail: return r; } /* * coolkey_read_binary will read a coolkey object off the card. That object is selected * by select file. If we've already read the object, we'll return the data from the cache. * coolkey objects are encoded PKCS #11 entries, not pkcs #15 data. pkcs15-coolkey will * 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) { coolkey_private_data_t * priv = COOLKEY_DATA(card); int r = 0; size_t len; u8 *data = NULL; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (idx > priv->obj->length) { 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_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); LOG_FUNC_RETURN(card->ctx, (int)len); } sc_log(card->ctx, "clearing cache idx=%u count=%"SC_FORMAT_LEN_SIZE_T"u", idx, count); data = malloc(priv->obj->length); if (data == NULL) { r = SC_ERROR_OUT_OF_MEMORY; goto done; } r = coolkey_read_object(card, priv->obj->id, 0, data, priv->obj->length, priv->nonce, sizeof(priv->nonce)); if (r < 0) goto done; if ((size_t) r != priv->obj->length) { priv->obj->length = r; } /* 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 = (int)len; /* cache the data in the object */ priv->obj->data=data; data = NULL; done: if (data) free(data); LOG_FUNC_RETURN(card->ctx, r); } /* COOLKEY driver is read only. NOTE: The applet supports w/r operations, so it's perfectly * reasonable to try to create new objects, but currently TPS does not create applets * That allow user created objects, so this is a nice 2.0 feature. */ static int coolkey_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); LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } /* initialize getting a list and return the number of elements in the list */ static int coolkey_get_init_and_get_count(list_t *list, int *countp) { *countp = list_size(list); list_iterator_start(list); return SC_SUCCESS; } /* fill in the obj_info for the current object on the list and advance to the next object */ static int coolkey_fetch_object(list_t *list, sc_cardctl_coolkey_object_t *coolkey_obj) { sc_cardctl_coolkey_object_t *ptr; if (!list_iterator_hasnext(list)) { return SC_ERROR_FILE_END_REACHED; } ptr = list_iterator_next(list); *coolkey_obj = *ptr; return SC_SUCCESS; } /* Finalize iterator */ static int coolkey_final_iterator(list_t *list) { list_iterator_stop(list); return SC_SUCCESS; } static char * coolkey_cuid_to_string(coolkey_cuid_t *cuid) { char *buf; size_t len = sizeof(coolkey_cuid_t)*2 + 1; buf = malloc(len); if (buf == NULL) { return NULL; } sc_bin_to_hex((u8 *)cuid, sizeof(*cuid), buf, len, 0); return buf; } static const struct manufacturer_list_st { unsigned short id; char *string; } manufacturer_list[] = { { 0x2050, "%04x Oberthur" }, { 0x4090, "%04x GemAlto (Infineon)" }, { 0x4780, "%04x STMicroElectronics" }, { 0x4780, "%04x RSA" }, { 0x534e, "%04x SafeNet" }, }; int manufacturer_list_count = sizeof(manufacturer_list)/sizeof(manufacturer_list[0]); static char * coolkey_get_manufacturer(coolkey_cuid_t *cuid) { unsigned short fabricator = bebytes2ushort(cuid->ic_fabricator); int i; char *buf; const char *manufacturer_string = "%04x Unknown"; size_t len; int r; for (i=0; i < manufacturer_list_count; i++) { if (manufacturer_list[i].id == fabricator) { manufacturer_string = manufacturer_list[i].string; break; } } len = strlen(manufacturer_string)+1; buf= malloc(len); if (buf == NULL) { return NULL; } r = snprintf(buf, len, manufacturer_string, fabricator); if (r < 0) { free(buf); return NULL; } return buf; } static int coolkey_get_token_info(sc_card_t *card, sc_pkcs15_tokeninfo_t * token_info) { coolkey_private_data_t * priv = COOLKEY_DATA(card); char *label = NULL; char *manufacturer_id = NULL; char *serial_number = NULL; 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; } free(label); free(manufacturer_id); free(serial_number); return SC_ERROR_OUT_OF_MEMORY; } static int coolkey_get_serial_nr_from_CUID(sc_card_t* card, sc_serial_number_t* serial) { coolkey_private_data_t * priv = COOLKEY_DATA(card); LOG_FUNC_CALLED(card->ctx); memcpy(serial->value, &priv->cuid, sizeof(priv->cuid)); serial->len = sizeof(priv->cuid); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } int coolkey_fill_object(sc_card_t *card, sc_cardctl_coolkey_object_t *obj) { int r; size_t buf_len = obj->length; u8 *new_obj_data = NULL; 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; } new_obj_data = malloc(buf_len); if (new_obj_data == NULL) { return SC_ERROR_OUT_OF_MEMORY; } r = coolkey_read_object(card, obj->id, 0, new_obj_data, buf_len, priv->nonce, sizeof(priv->nonce)); if (r != (int)buf_len) { free(new_obj_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) { free(new_obj_data); return SC_ERROR_INTERNAL; /* shouldn't happen */ } if (obj_entry->data != NULL) { 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; LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } /* * return a parsed record for the attribute which includes value, type, and length. * Handled both v1 and v0 record types. determine record type from the object. * make sure we don't overrun the buffer if the token gives us bad data. */ static int coolkey_find_attribute(sc_card_t *card, sc_cardctl_coolkey_attribute_t *attribute) { u8 object_record_type; CK_ATTRIBUTE_TYPE attr_type = attribute->attribute_type; const u8 *obj = attribute->object->data; const u8 *attr = NULL; size_t buf_len = attribute->object->length; coolkey_object_header_t *object_head; int attribute_count,i; attribute->attribute_data_type = SC_CARDCTL_COOLKEY_ATTR_TYPE_STRING; 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); if (r < 0) { 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)); /* 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; } object_head = (coolkey_object_header_t *)obj; object_record_type = object_head->record_type; /* make sure it's a type we recognize */ if ((object_record_type != COOLKEY_V1_OBJECT) && (object_record_type != COOLKEY_V0_OBJECT)) { return SC_ERROR_CORRUPTED_DATA; } /* * now loop through all the attributes in the list. first find the start of the list */ attr = coolkey_attribute_start(obj, object_record_type, buf_len); if (attr == NULL) { return SC_ERROR_CORRUPTED_DATA; } buf_len -= (attr-obj); /* now get the count */ attribute_count = coolkey_get_attribute_count(obj, object_record_type, buf_len); 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 || 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)) { /* yup, return it */ return coolkey_get_attribute_data(attr, object_record_type, record_len, attribute); } /* go to the next attribute on the list */ buf_len -= record_len; attr += record_len; } /* not find in attribute list, check the fixed attribute record */ if (object_record_type == COOLKEY_V1_OBJECT) { unsigned long fixed_attributes = bebytes2ulong(object_head->fixed_attributes_values); return coolkey_get_attribute_data_fixed(attr_type, fixed_attributes, attribute); } LOG_FUNC_RETURN(card->ctx, SC_ERROR_DATA_OBJECT_NOT_FOUND); } /* * pkcs 15 needs to find the cert matching the keys to fill in some of the fields that wasn't stored * with the key. To do this we need to look for the cert matching the key's CKA_ID. For flexibility, * We simply search using a pkcs #11 style template using the cardctl_coolkey_attribute_t structure */ sc_cardctl_coolkey_object_t * coolkey_find_object_by_template(sc_card_t *card, sc_cardctl_coolkey_attribute_t *template, int count) { list_t *list; sc_cardctl_coolkey_object_t *current, *rv = NULL; coolkey_private_data_t * priv = COOLKEY_DATA(card); int i, r; unsigned int tmp_pos = (unsigned int) -1; list = &priv->objects_list; if (list->iter_active) { /* workaround missing functionality of second iterator */ tmp_pos = list->iter_pos; list_iterator_stop(list); } list_iterator_start(list); while (list_iterator_hasnext(list)) { sc_cardctl_coolkey_attribute_t attribute; current = list_iterator_next(list); attribute.object = current; for (i=0; i < count; i++) { attribute.attribute_type = template[i].attribute_type; r = coolkey_find_attribute(card, &attribute); if (r < 0) { break; } if (template[i].attribute_data_type != attribute.attribute_data_type) { break; } if (template[i].attribute_length != attribute.attribute_length) { break; } if (memcmp(attribute.attribute_value, template[i].attribute_value, attribute.attribute_length) != 0) { break; } } /* just return the first one */ if (i == count) { rv = current; break; } } list_iterator_stop(list); if (tmp_pos != (unsigned int)-1) { /* workaround missing functionality of second iterator */ list_iterator_start(list); while (list_iterator_hasnext(list) && list->iter_pos < tmp_pos) (void) list_iterator_next(list); } return rv; } static int coolkey_find_object(sc_card_t *card, sc_cardctl_coolkey_find_object_t *fobj) { sc_cardctl_coolkey_object_t *obj = NULL; coolkey_private_data_t * priv = COOLKEY_DATA(card); int r; switch (fobj->type) { case SC_CARDCTL_COOLKEY_FIND_BY_ID: obj = coolkey_find_object_by_id(&priv->objects_list, fobj->find_id); break; case SC_CARDCTL_COOLKEY_FIND_BY_TEMPLATE: obj = coolkey_find_object_by_template(card, fobj->coolkey_template, fobj->template_count); break; default: break; } if (obj == NULL) { return SC_ERROR_DATA_OBJECT_NOT_FOUND; } if (obj->data == NULL) { r = coolkey_fill_object(card, obj); if (r < 0) { return r; } } fobj->obj = obj; return SC_SUCCESS; } static int coolkey_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) { coolkey_private_data_t * priv = COOLKEY_DATA(card); 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 coolkey_get_serial_nr_from_CUID(card, (sc_serial_number_t *) ptr); case SC_CARDCTL_COOLKEY_GET_TOKEN_INFO: return coolkey_get_token_info(card, (sc_pkcs15_tokeninfo_t *) ptr); case SC_CARDCTL_COOLKEY_FIND_OBJECT: return coolkey_find_object(card, (sc_cardctl_coolkey_find_object_t *)ptr); case SC_CARDCTL_COOLKEY_INIT_GET_OBJECTS: return coolkey_get_init_and_get_count(&priv->objects_list, (int *)ptr); case SC_CARDCTL_COOLKEY_GET_NEXT_OBJECT: return coolkey_fetch_object(&priv->objects_list, (sc_cardctl_coolkey_object_t *)ptr); case SC_CARDCTL_COOLKEY_FINAL_GET_OBJECTS: return coolkey_final_iterator(&priv->objects_list); case SC_CARDCTL_COOLKEY_GET_ATTRIBUTE: return coolkey_find_attribute(card,(sc_cardctl_coolkey_attribute_t *)ptr); } LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } static int coolkey_get_challenge(sc_card_t *card, u8 *rnd, size_t len) { LOG_FUNC_CALLED(card->ctx); if (len > COOLKEY_MAX_CHUNK_SIZE) len = COOLKEY_MAX_CHUNK_SIZE; 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) { int r = SC_SUCCESS; coolkey_private_data_t * priv = COOLKEY_DATA(card); SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); 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); if ((env->algorithm != SC_ALGORITHM_RSA) && (env->algorithm != SC_ALGORITHM_EC)) { r = SC_ERROR_NO_CARD_SUPPORT; } priv->algorithm = env->algorithm; priv->operation = env->operation; SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } static int coolkey_restore_security_env(sc_card_t *card, int se_num) { SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } #define MAX_COMPUTE_BUF 200 typedef struct coolkey_compute_crypt_init_params { u8 mode; u8 direction; u8 location; u8 buf_len[2]; } coolkey_compute_crypt_init_params_t; typedef struct coolkey_compute_crypt_params { coolkey_compute_crypt_init_params_t init; u8 buf[MAX_COMPUTE_BUF]; } coolkey_compute_crypt_params_t; typedef struct coolkey_compute_ecc_params { u8 location; u8 buf_len[2]; 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) { int r; 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_out; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_log(card->ctx, "datalen=%"SC_FORMAT_LEN_SIZE_T"u outlen=%"SC_FORMAT_LEN_SIZE_T"u\n", datalen, max_out_len); if (priv->key_id > 0xff) { r = SC_ERROR_NO_DEFAULT_KEY; goto done; } key_number = priv->key_id; 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 (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); ushort2bebytes(len_buf, datalen); r = coolkey_write_object(card, COOLKEY_DL_OBJECT_ID, 0, len_buf, sizeof(len_buf), priv->nonce, sizeof(priv->nonce)); if (r < 0) { goto done; } 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; 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)); if (r < 0) { goto done; } buf_len = crypt_out_len_p; if (datalen > MAX_COMPUTE_BUF) { u8 len_buf[2]; size_t out_length; r = coolkey_read_object(card, COOLKEY_DL_OBJECT_ID, 0, len_buf, sizeof(len_buf), priv->nonce, sizeof(priv->nonce)); if (r < 0) { goto done; } out_length = bebytes2ushort(len_buf); out_length = MIN(out_length,max_out_len); r = coolkey_read_object(card, COOLKEY_DL_OBJECT_ID, sizeof(len_buf), out, out_length, priv->nonce, sizeof(priv->nonce)); } else { size_t out_length; if (buf_len < 2) { r = SC_ERROR_WRONG_LENGTH; goto done; } out_length = bebytes2ushort(buf); if (out_length > sizeof buf - 2) { r = SC_ERROR_WRONG_LENGTH; goto done; } out_length = MIN(out_length, max_out_len); memcpy(out, buf + 2, out_length); r = (int)out_length; } done: return r; } static int coolkey_ecc_op(sc_card_t *card, const u8 * data, size_t datalen, u8 * out, size_t outlen) { int r; const u8 *crypt_in; u8 **crypt_out_p; u8 ins = 0; size_t crypt_in_len, *crypt_out_len_p; coolkey_private_data_t * priv = COOLKEY_DATA(card); coolkey_compute_ecc_params_t params; size_t params_len; u8 key_number; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_log(card->ctx, "datalen=%"SC_FORMAT_LEN_SIZE_T"u outlen=%"SC_FORMAT_LEN_SIZE_T"u\n", datalen, outlen); crypt_in = data; crypt_in_len = datalen; crypt_out_p = &out; crypt_out_len_p = &outlen; key_number = priv->key_id; params.location = COOLKEY_CRYPT_LOCATION_APDU; if (priv->key_id > 0xff) { r = SC_ERROR_NO_DEFAULT_KEY; goto done; } switch (priv->operation) { case SC_SEC_OPERATION_DERIVE: ins = COOLKEY_INS_COMPUTE_ECC_KEY_AGREEMENT; break; case SC_SEC_OPERATION_SIGN: ins = COOLKEY_INS_COMPUTE_ECC_SIGNATURE; break; default: r = SC_ERROR_NOT_SUPPORTED; goto done; } params_len = (sizeof(params) - sizeof(params.buf)) + crypt_in_len; ushort2bebytes(params.buf_len, crypt_in_len); if (crypt_in_len) { memcpy(params.buf, crypt_in, crypt_in_len); } r = coolkey_apdu_io(card, COOLKEY_CLASS, ins, key_number, COOLKEY_CRYPT_ONE_STEP, (u8 *)¶ms, params_len, crypt_out_p, crypt_out_len_p, priv->nonce, sizeof(priv->nonce)); done: return r; } static int coolkey_compute_crypt(sc_card_t *card, const u8 * data, size_t datalen, u8 * out, size_t outlen) { coolkey_private_data_t * priv = COOLKEY_DATA(card); int r; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); switch (priv->algorithm) { case SC_ALGORITHM_RSA: r = coolkey_rsa_op(card, data, datalen, out, outlen); break; case SC_ALGORITHM_EC: r = coolkey_ecc_op(card, data, datalen, out, outlen); break; default: r = SC_ERROR_NO_CARD_SUPPORT; break; } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } static u8 coolkey_class(unsigned long object_id) { return (object_id >> 24) & 0xff; } static unsigned short coolkey_get_key_id(unsigned long object_id) { char char_index = (object_id >> 16) & 0xff; if (char_index >= '0' && char_index <= '9') { return (u8)(char_index - '0'); } if (char_index >= 'A' && char_index <= 'Z') { return (u8)(char_index - 'A' + 10); } if (char_index >= 'a' && char_index <= 'z') { return (u8)(char_index - 'a' + 26 + 10); } return COOLKEY_INVALID_KEY; } /* * 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. * * This select simply records what object was selected so that read knows how to access it. */ static int coolkey_select_file(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file_out) { int r; struct sc_file *file = NULL; coolkey_private_data_t * priv = COOLKEY_DATA(card); unsigned long object_id; assert(card != NULL && in_path != NULL); SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (in_path->len != 4) { return SC_ERROR_OBJECT_NOT_FOUND; } r = coolkey_select_applet(card); if (r != SC_SUCCESS) { return r; } object_id = bebytes2ulong(in_path->value); priv->obj = coolkey_find_object_by_id(&priv->objects_list, object_id); if (priv->obj == NULL) { return SC_ERROR_OBJECT_NOT_FOUND; } priv->key_id = COOLKEY_INVALID_KEY; if (coolkey_class(object_id) == COOLKEY_KEY_CLASS) { priv->key_id = coolkey_get_key_id(object_id); } if (file_out) { file = sc_file_new(); if (file == NULL) 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; file->shareable = 0; file->ef_structure = 0; file->size = priv->obj->length; *file_out = file; } return SC_SUCCESS; } static int coolkey_finish(sc_card_t *card) { coolkey_private_data_t * priv = COOLKEY_DATA(card); SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (priv) { coolkey_free_private_data(priv); } return SC_SUCCESS; } static int coolkey_add_object(coolkey_private_data_t *priv, unsigned long object_id, const u8 *object_data, size_t object_length, int add_v1_record) { sc_cardctl_coolkey_object_t new_object; int r; memset(&new_object, 0, sizeof(new_object)); new_object.path = coolkey_template_path; new_object.path.len = 4; ulong2bebytes(new_object.path.value, object_id); 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) { return SC_ERROR_OUT_OF_MEMORY; } if (add_v1_record) { new_object.data[0] = COOLKEY_V1_OBJECT; new_object.length++; } memcpy(&new_object.data[add_v1_record], object_data, object_length); } r = coolkey_add_object_to_list(&priv->objects_list, &new_object); if (r != SC_SUCCESS) { /* if we didn't successfully put the object on the list, * the data space didn't get adopted. free it before we return */ free(new_object.data); new_object.data = NULL; } return r; } static int coolkey_process_combined_object(sc_card_t *card, coolkey_private_data_t *priv, u8 *object, size_t object_length) { coolkey_combined_header_t *header = (coolkey_combined_header_t *)object; unsigned short compressed_offset; unsigned short compressed_length; unsigned short compressed_type; unsigned short object_offset; unsigned short object_count; coolkey_decompressed_header_t *decompressed_header; u8 *decompressed_object = NULL; size_t decompressed_object_len = 0; int free_decompressed = 0; int i, r; if (object_length < sizeof(coolkey_combined_header_t)) { return SC_ERROR_CORRUPTED_DATA; } compressed_offset = bebytes2ushort(header->compression_offset); compressed_length = bebytes2ushort(header->compression_length); compressed_type = bebytes2ushort(header->compression_type); if ((((size_t)compressed_offset) + (size_t)compressed_length) > object_length) { return SC_ERROR_CORRUPTED_DATA; } /* store the CUID */ memcpy(&priv->cuid, &header->cuid, sizeof(priv->cuid)); if (compressed_type == COOLKEY_COMPRESSION_ZLIB) { #ifdef ENABLE_ZLIB r = sc_decompress_alloc(&decompressed_object, &decompressed_object_len, &object[compressed_offset], compressed_length, COMPRESSION_AUTO); if (r) goto done; free_decompressed = 1; #else sc_log(card->ctx, "Coolkey compression not supported, no zlib"); LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); #endif } else { decompressed_object =&object[compressed_offset]; decompressed_object_len = (size_t) compressed_length; } decompressed_header = (coolkey_decompressed_header_t *)decompressed_object; if (decompressed_object_len < sizeof(coolkey_decompressed_header_t)) { r = SC_ERROR_CORRUPTED_DATA; goto done; } object_offset = bebytes2ushort(decompressed_header->object_offset); object_count = bebytes2ushort(decompressed_header->object_count); /* * using 2 different tests here so we can log different errors if logging is * turned on. */ /* 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) { 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) { 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; goto done; } 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_length = decompressed_header->token_name_length; 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) { goto done; } if ((size_t)r + object_offset > decompressed_object_len) { r = SC_ERROR_CORRUPTED_DATA; goto done; } current_object_len = r; 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; } } r = SC_SUCCESS; done: if (free_decompressed) { free(decompressed_object); } return r; } static int coolkey_list_object(sc_card_t *card, u8 seq, coolkey_object_info_t *object_info) { u8 *rbuf = (u8 *) object_info; size_t rbuflen = sizeof(*object_info); return coolkey_apdu_io(card, COOLKEY_CLASS, COOLKEY_INS_LIST_OBJECTS, seq, 0, NULL, 0, &rbuf, &rbuflen, NULL, 0); } /* * Initialize the Coolkey data structures. */ static int coolkey_initialize(sc_card_t *card) { int r; coolkey_private_data_t *priv = NULL; coolkey_life_cycle_t life_cycle; coolkey_object_info_t object_info; int combined_processed = 0; /* already found? */ if (card->drv_data) { return SC_SUCCESS; } sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"Coolkey Applet found"); priv = coolkey_new_private_data(); if (priv == NULL) { r = SC_ERROR_OUT_OF_MEMORY; goto cleanup; } r = coolkey_get_life_cycle(card, &life_cycle); 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 */ 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 * compressed greatly increasing the effectiveness of compress (since lots * of certs on the token share the same Subject and Issuer DN's). We now * process it separately so that we can have both combined objects managed * by TPS and user managed certs on the same token */ if (object_id == COOLKEY_COMBINED_OBJECT_ID) { u8 *object = malloc(object_len); if (object == NULL) { r = SC_ERROR_OUT_OF_MEMORY; break; } r = coolkey_read_object(card, COOLKEY_COMBINED_OBJECT_ID, 0, object, object_len, priv->nonce, sizeof(priv->nonce)); if (r < 0) { free(object); break; } r = coolkey_process_combined_object(card, priv, object, r); free(object); if (r != SC_SUCCESS) { break; } combined_processed = 1; } 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); } /* 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; /* 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; goto cleanup; } priv->token_name_length = sizeof("COOLKEY")-1; } card->drv_data = priv; LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); cleanup: if (priv) { coolkey_free_private_data(priv); } LOG_FUNC_RETURN(card->ctx, r); } /* NOTE: returns a bool, 1 card matches, 0 it does not */ static int coolkey_match_card(sc_card_t *card) { int r; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); r = coolkey_select_applet(card); 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; } static int coolkey_init(sc_card_t *card) { int r; unsigned long flags; unsigned long ext_flags; coolkey_private_data_t * priv; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); r = coolkey_initialize(card); if (r < 0) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_CARD); } card->type = SC_CARD_TYPE_COOLKEY_GENERIC; /* set Token Major/minor version */ 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 */ 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, 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); priv = COOLKEY_DATA(card); if (priv->pin_count != 0) { card->caps |= SC_CARD_CAP_ISO7816_PIN_INFO; } 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) { int r; coolkey_private_data_t * priv = COOLKEY_DATA(card); size_t rbuflen; u8 *rbuf; /* COOLKEY uses a separate pin from the card pin, managed by the applet. * if we successfully log into coolkey, we will get a nonce, which we append * to our APDUs to authenticate the apdu to the card. This allows coolkey to * maintain separate per application login states without the application * having to cache the pin */ switch (data->cmd) { case SC_PIN_CMD_GET_INFO: if (priv->nonce_valid) { data->pin1.logged_in = SC_PIN_STATE_LOGGED_IN; } else { data->pin1.logged_in = SC_PIN_STATE_LOGGED_OUT; /* coolkey retries is 100. It's unlikely the pin is block. * instead, coolkey slows down the login command exponentially */ data->pin1.tries_left = 0xf; } if (tries_left) { *tries_left = data->pin1.tries_left; } r = SC_SUCCESS; break; case SC_PIN_CMD_UNBLOCK: case SC_PIN_CMD_CHANGE: /* these 2 commands are currently reserved for TPS */ default: r = SC_ERROR_NOT_SUPPORTED; break; case SC_PIN_CMD_VERIFY: /* coolkey applet supports multiple pins, but TPS currently only uses one. * just support the one pin for now (we need an array of nonces to handle * multiple pins) */ /* coolkey only supports unpadded ascii pins, so no need to format the pin */ rbuflen = sizeof(priv->nonce); rbuf = &priv->nonce[0]; r = coolkey_apdu_io(card, COOLKEY_CLASS, COOLKEY_INS_VERIFY_PIN, data->pin_reference, 0, data->pin1.data, data->pin1.len, &rbuf, &rbuflen, NULL, 0); if (r < 0) { break; } priv->nonce_valid = 1; r = SC_SUCCESS; } return r; } static int coolkey_logout(sc_card_t *card) { /* when we add multi pin support here, how do we know which pin to logout? */ coolkey_private_data_t * priv = COOLKEY_DATA(card); u8 pin_ref = 0; (void) coolkey_apdu_io(card, COOLKEY_CLASS, COOLKEY_INS_LOGOUT, pin_ref, 0, NULL, 0, NULL, NULL, priv->nonce, sizeof(priv->nonce)); /* even if logout failed on the card, flush the nonce and clear the nonce_valid and we are effectively * logged out... needing to login again to get a nonce back */ memset(priv->nonce, 0, sizeof(priv->nonce)); priv->nonce_valid = 0; 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 = { "COOLKEY", "coolkey", &coolkey_ops, NULL, 0, NULL }; static struct sc_card_driver * sc_get_driver(void) { struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); coolkey_ops = *iso_drv->ops; coolkey_ops.match_card = coolkey_match_card; coolkey_ops.init = coolkey_init; coolkey_ops.finish = coolkey_finish; coolkey_ops.select_file = coolkey_select_file; /* need to record object type */ coolkey_ops.get_challenge = coolkey_get_challenge; coolkey_ops.read_binary = coolkey_read_binary; coolkey_ops.write_binary = coolkey_write_binary; coolkey_ops.set_security_env = coolkey_set_security_env; coolkey_ops.restore_security_env = coolkey_restore_security_env; coolkey_ops.compute_signature = coolkey_compute_crypt; coolkey_ops.decipher = coolkey_compute_crypt; coolkey_ops.card_ctl = coolkey_card_ctl; 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; } struct sc_card_driver * sc_get_coolkey_driver(void) { return sc_get_driver(); } OpenSC-0.26.1/src/libopensc/card-default.c000066400000000000000000000033101474147347300202060ustar00rootroot00000000000000/* * card-default.c: Support for cards with no driver * * Copyright (C) 2001, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "internal.h" static struct sc_card_operations default_ops; static struct sc_card_driver default_drv = { "Default driver for unknown cards", "default", &default_ops, NULL, 0, NULL }; static int default_match_card(struct sc_card *card) { return 1; /* always match */ } static int default_init(struct sc_card *card) { LOG_FUNC_CALLED(card->ctx); card->name = "Unsupported card"; card->drv_data = NULL; LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } static struct sc_card_driver * sc_get_driver(void) { struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); default_ops = *iso_drv->ops; default_ops.match_card = default_match_card; default_ops.init = default_init; return &default_drv; } struct sc_card_driver * sc_get_default_driver(void) { return sc_get_driver(); } OpenSC-0.26.1/src/libopensc/card-dnie.c000066400000000000000000002162101474147347300175060ustar00rootroot00000000000000/** * card-dnie.c: Support for Spanish DNI electronico (DNIe card). * * 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 * Direccion General de la Policia y de la Guardia Civil * * 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 */ #define __CARD_DNIE_C__ #ifdef HAVE_CONFIG_H #include "config.h" #endif #if defined(ENABLE_OPENSSL) && defined(ENABLE_SM) /* empty file without openssl or sm */ #include #include #include #include #include "opensc.h" #include "cardctl.h" #include "internal.h" #include "cwa14890.h" #include "cwa-dnie.h" #ifdef _WIN32 #ifndef UNICODE #define UNICODE #endif #include #endif #ifdef __APPLE__ #include #endif #define MAX_RESP_BUFFER_SIZE 2048 /* default titles */ #define USER_CONSENT_TITLE "Confirm" extern int dnie_read_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" #define DNIE_CHIP_SHORTNAME "dnie" #define DNIE_MF_NAME "Master.File" /* default user consent program (if required) */ #define USER_CONSENT_CMD "/usr/bin/pinentry" /** * 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 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 TS 0x3B Direct Convention T0 0x7F Y1=0x07=0111; TA1,TB1 y TC1 present. K=0x0F=1111; 15 historical bytes TA1 0x38 FI (Factor de conversión de la tasa de reloj) = 744 DI (Factor de ajuste de la tasa de bits) = 12 Máximo 8 Mhz. TB1 0x00 Vpp (voltaje de programación) no requerido. TC1 0x00 No se requiere tiempo de espera adicional. H1 0x00 No usado H2 0x6A Datos de preexpedición. Diez bytes con identificación del expedidor. H3 0x44 'D' H4 0x4E 'N' H5 0x49 'I' H6 0x65 'e' H7 Fabricante de la tecnología Match-on-Card incorporada. 0x10 SAGEM 0x20 SIEMENS H8 0x02 Fabricante del CI: STMicroelectronics. H9 0x4C H10 0x34 Tipo de CI: 19WL34 H11 0x01 MSB de la version del SO: 1 H12 0x1v LSB de la version del SO: 1v H13 Fase del ciclo de vida . 0x00 prepersonalización. 0x01 personalización. 0x03 usuario. 0x0F final. H14 0xss H15 0xss Bytes de estado H13-H15: 0x03 0x90 0x00 user phase: tarjeta operativa H13-H15: 0x0F 0x65 0x81 final phase: tarjeta no operativa */ /** * ATR Table list. * OpenDNIe defines two ATR's for user and finalized card state */ static struct sc_atr_table dnie_atrs[] = { /* 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", DNIE_CHIP_SHORTNAME, SC_CARD_TYPE_DNIE_USER, 0, NULL}, { /** card finalized, unusable */ "3B:7F:00:00:00:00:6A:44:4E:49:65:00:00:00:00:00:00:0F:65:81", "FF:FF:00:FF:FF:FF:FF:FF:FF:FF:FF:00:00:00:00:00:00:FF:FF:FF", DNIE_CHIP_SHORTNAME, SC_CARD_TYPE_DNIE_TERMINATED, 0, NULL}, {NULL, NULL, NULL, 0, 0, NULL} }; /** * Messages used on user consent procedures */ const char *user_consent_title="Signature Requested"; #ifdef linux const char *user_consent_message="Está a punto de realizar una firma electrónica con su clave de FIRMA del DNI electrónico. ¿Desea permitir esta operación?"; #else const char *user_consent_message="Esta a punto de realizar una firma digital\ncon su clave de FIRMA del DNI electronico.\nDesea permitir esta operacion?"; #endif #ifdef ENABLE_DNIE_UI /** * Messages used on pinentry protocol */ char *user_consent_msgs[] = { "SETTITLE", "SETDESC", "CONFIRM", "BYE" }; #if !defined(__APPLE__) && !defined(_WIN32) /** * Do fgets() without interruptions. * * Retry the operation if it is interrupted, such as with receiving an alarm. * * @param s Buffer receiving the data * @param size Size of the buffer * @param stream Stream to read * @return s on success, NULL on error */ static char *nointr_fgets(char *s, int size, FILE *stream) { while (fgets(s, size, stream) == NULL) { if (feof(stream) || errno != EINTR) return NULL; } return s; } #endif /** * Ask for user consent. * * Check for user consent configuration, * Invoke proper gui app and check result * * @param card pointer to sc_card structure * @param title Text to appear in the window header * @param text Message to show to the user * @return SC_SUCCESS on user consent OK , else error code */ int dnie_ask_user_consent(struct sc_card * card, const char *title, const char *message) { #ifdef __APPLE__ CFOptionFlags result; /* result code from the message box */ /* convert the strings from char* to CFStringRef */ CFStringRef header_ref; /* to store title */ CFStringRef message_ref; /* to store message */ #endif #if !defined(__APPLE__) && !defined(_WIN32) pid_t pid; FILE *fin=NULL; FILE *fout=NULL; /* to handle pipes as streams */ struct stat st_file; /* to verify that executable exists */ int srv_send[2]; /* to send data from server to client */ int srv_recv[2]; /* to receive data from client to server */ char outbuf[1024]; /* to compose and send messages */ char buf[1024]; /* to store client responses */ int n = 0; /* to iterate on to-be-sent messages */ #endif int res = SC_ERROR_INTERNAL; /* by default error :-( */ char *msg = NULL; /* to mark errors */ if ((card == NULL) || (card->ctx == NULL)) return SC_ERROR_INVALID_ARGUMENTS; LOG_FUNC_CALLED(card->ctx); if ((title==NULL) || (message==NULL)) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); if (GET_DNIE_UI_CTX(card).user_consent_enabled == 0 || card->ctx->flags & SC_CTX_FLAG_DISABLE_POPUPS) { sc_log(card->ctx, "User Consent or popups are disabled in configuration file"); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } #ifdef _WIN32 /* in Windows, do not use pinentry, but MessageBox system call */ res = MessageBox ( NULL, TEXT(message), TEXT(title), MB_ICONWARNING | MB_OKCANCEL | MB_DEFBUTTON2 | MB_APPLMODAL ); if ( res == IDOK ) LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_ALLOWED); #elif __APPLE__ /* Also in Mac OSX use native functions */ /* convert the strings from char* to CFStringRef */ header_ref = CFStringCreateWithCString( NULL, title, strlen(title) ); message_ref = CFStringCreateWithCString( NULL,message, strlen(message) ); /* Display user notification alert */ CFUserNotificationDisplayAlert( 0, /* no timeout */ kCFUserNotificationNoteAlertLevel, /* Alert level */ NULL, /* IconURL, use default, you can change */ /* it depending message_type flags */ NULL, /* SoundURL (not used) */ NULL, /* localization of strings */ header_ref, /* header. Cannot be null */ message_ref, /* message text */ CFSTR("Cancel"), /* default ( "OK" if null) button text */ CFSTR("OK"), /* second button title */ NULL, /* third button title, null--> no other button */ &result /* response flags */ ); /* Clean up the strings */ CFRelease( header_ref ); CFRelease( message_ref ); /* Return 0 only if "OK" is selected */ if( result == kCFUserNotificationAlternateResponse ) LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_ALLOWED); #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) { msg = "pipe(srv_send)"; goto do_error; } if (pipe(srv_recv) < 0) { msg = "pipe(srv_recv)"; goto do_error; } pid = fork(); switch (pid) { case -1: /* error */ msg = "fork()"; goto do_error; case 0: /* child */ /* make our pipes, our new stdin & stderr, closing older ones */ dup2(srv_send[0], STDIN_FILENO); /* map srv send for input */ dup2(srv_recv[1], STDOUT_FILENO); /* map srv_recv for output */ /* once dup2'd pipes are no longer needed on client; so close */ close(srv_send[0]); close(srv_send[1]); close(srv_recv[0]); close(srv_recv[1]); /* 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 * so when we close the others, an EOF will be transmitted properly. */ close(srv_send[0]); close(srv_recv[1]); /* use iostreams to take care on newlines and text based data */ fin = fdopen(srv_recv[0], "r"); if (fin == NULL) { msg = "fdopen(in)"; goto do_error; } fout = fdopen(srv_send[1], "w"); if (fout == NULL) { msg = "fdopen(out)"; goto do_error; } /* read and ignore first line */ if (nointr_fgets(buf, sizeof(buf), fin) == NULL) { res = SC_ERROR_INTERNAL; msg = "nointr_fgets() Unexpected IOError/EOF"; goto do_error; } for (n = 0; n<4; n++) { char *pt; if (n==0) snprintf(outbuf, sizeof outbuf,"%s %s\n",user_consent_msgs[0],title); else if (n==1) snprintf(outbuf, sizeof outbuf,"%s %s\n",user_consent_msgs[1],message); else snprintf(outbuf, sizeof outbuf,"%s\n",user_consent_msgs[n]); /* send message */ fputs(outbuf, fout); fflush(fout); /* get response */ pt=nointr_fgets(buf, sizeof(buf), fin); if (pt==NULL) { res = SC_ERROR_INTERNAL; msg = "nointr_fgets() Unexpected IOError/EOF"; goto do_error; } if (strstr(buf, "OK") == NULL) { res = SC_ERROR_NOT_ALLOWED; msg = "fail/cancel"; goto do_error; } } } /* switch */ /* arriving here means signature has been accepted by user */ res = SC_SUCCESS; msg = NULL; do_error: /* close out channel to force client receive EOF and also die */ if (fout != NULL) fclose(fout); if (fin != NULL) fclose(fin); #endif if (msg != NULL) sc_log(card->ctx, "%s", msg); LOG_FUNC_RETURN(card->ctx, res); } #endif /* ENABLE_DNIE_UI */ /** * DNIe specific card driver operations */ static struct sc_card_operations dnie_ops; /** * Local copy of iso7816 card driver operations */ static struct sc_card_operations *iso_ops = NULL; /** * Module definition for OpenDNIe card driver */ static sc_card_driver_t dnie_driver = { DNIE_CHIP_NAME, /**< Full name for DNIe card driver */ DNIE_CHIP_SHORTNAME, /**< Short name for DNIe card driver */ &dnie_ops, /**< pointer to dnie_ops (DNIe card driver operations) */ dnie_atrs, /**< List of card ATR's handled by this driver */ 0, /**< (natrs) number of atr's to check for this driver */ NULL /**< (dll) Card driver module (on DNIe is null) */ }; /************************** card-dnie.c internal functions ****************/ /** * Parse configuration file for dnie parameters. * * 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 * @param card Pointer to card structure * @param ui_context Pointer to ui_context structure to store data into * @return SC_SUCCESS (should return no errors) * * TODO: Code should be revised in order to store user consent info * in a card-independent way at configuration file */ #ifdef ENABLE_DNIE_UI static int dnie_get_environment( sc_card_t * card, ui_context_t * ui_context) { int i; scconf_block **blocks, *blk; sc_context_t *ctx; /* set default values */ ui_context->user_consent_app = USER_CONSENT_CMD; ui_context->user_consent_enabled = 1; /* look for sc block in opensc.conf */ ctx = card->ctx; for (i = 0; ctx->conf_blocks[i]; i++) { blocks = scconf_find_blocks(ctx->conf, ctx->conf_blocks[i], "card_driver", "dnie"); if (!blocks) continue; blk = blocks[0]; free(blocks); if (blk == NULL) continue; /* fill private data with configuration parameters */ ui_context->user_consent_app = /* def user consent app is "pinentry" */ (char *)scconf_get_str(blk, "user_consent_app", USER_CONSENT_CMD); ui_context->user_consent_enabled = /* user consent is enabled by default */ scconf_get_bool(blk, "user_consent_enabled", 1); } return SC_SUCCESS; } #endif /************************** 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 * this operation is available also outside. So need to test :-) * 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 * @param data where to store function results * @return SC_SUCCESS if ok, else error code */ static int dnie_generate_key(sc_card_t * card, void *data) { int result = SC_ERROR_NOT_SUPPORTED; if ((card == NULL) || (data == NULL)) return SC_ERROR_INVALID_ARGUMENTS; LOG_FUNC_CALLED(card->ctx); /* TODO: write dnie_generate_key() */ LOG_FUNC_RETURN(card->ctx, result); } /** * Analyze a buffer looking for provided data pattern. * * Commodity function for dnie_get_info() that searches a byte array * in provided buffer * * @param card pointer to card info data * @param pat data pattern to find in buffer * @param buf where to look for pattern * @param len buffer length * @return retrieved value or NULL if pattern not found * @see dnie_get_info() */ static char *findPattern(u8 *pat, u8 *buf, size_t len) { char *res = NULL; u8 *from = buf; int size = 0; /* Locate pattern. Assume pattern length=6 */ for ( from = buf; from < buf+len-6; from++) { if (memcmp(from,pat,6) == 0 ) goto data_found; } /* arriving here means pattern not found */ return NULL; data_found: /* assume length is less than 128 bytes, so is coded in 1 byte */ size = 0x000000ff & (int) *(from+6); if ( size == 0 ) return NULL; /* empty data */ res = calloc( size+1, sizeof(char) ); if ( res == NULL) return NULL; /* calloc() error */ memcpy(res,from+7,size); return res; } /** * Retrieve name, surname, and DNIe number. * * This is done by mean of reading and parsing CDF file * at address 3F0050156004 * 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), * we look for desired OID patterns in binary array * * @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 */ static int dnie_get_info(sc_card_t * card, char *data[]) { sc_file_t *file = NULL; sc_path_t path; u8 *buffer = NULL; size_t bufferlen = 0; char *msg = NULL; u8 SerialNumber [] = { 0x06, 0x03, 0x55, 0x04, 0x05, 0x13 }; u8 Name [] = { 0x06, 0x03, 0x55, 0x04, 0x04, 0x0C }; u8 GivenName [] = { 0x06, 0x03, 0x55, 0x04, 0x2A, 0x0C }; int res = SC_ERROR_NOT_SUPPORTED; if ((card == NULL) || (data == NULL)) return SC_ERROR_INVALID_ARGUMENTS; LOG_FUNC_CALLED(card->ctx); /* phase 1: get DNIe number, Name and GivenName */ /* read EF(CDF) at 3F0050156004 */ sc_format_path("3F0050156004", &path); res = dnie_read_file(card, &path, &file, &buffer, &bufferlen); if (res != SC_SUCCESS) { msg = "Cannot read EF(CDF)"; goto get_info_end; } /* locate OID 2.5.4.5 (SerialNumber) - DNIe number*/ data[0]= findPattern(SerialNumber,buffer,bufferlen); /* locate OID 2.5.4.4 (Name) - Apellidos */ data[1]= findPattern(Name,buffer,bufferlen); /* locate OID 2.5.4.42 (GivenName) - Nombre */ data[2]= findPattern(GivenName,buffer,bufferlen); if ( ! data[0] || !data[1] || !data[2] ) { res = SC_ERROR_INVALID_DATA; msg = "Cannot retrieve info from EF(CDF)"; goto get_info_end; } /* phase 2: get IDESP */ sc_format_path("3F000006", &path); sc_file_free(file); file = NULL; if (buffer) { free(buffer); buffer=NULL; bufferlen=0; } res = dnie_read_file(card, &path, &file, &buffer, &bufferlen); if (res != SC_SUCCESS) { data[3]=NULL; goto get_info_ph3; } data[3]=calloc(bufferlen+1,sizeof(char)); if ( !data[3] ) { msg = "Cannot allocate memory for IDESP data"; res = SC_ERROR_OUT_OF_MEMORY; goto get_info_end; } memcpy(data[3],buffer,bufferlen); get_info_ph3: /* phase 3: get DNIe software version */ sc_format_path("3F002F03", &path); sc_file_free(file); file = NULL; if (buffer) { free(buffer); buffer=NULL; bufferlen=0; } /* * Some old DNIe cards seems not to include SW version file, * so let this code fail without notice */ res = dnie_read_file(card, &path, &file, &buffer, &bufferlen); if (res != SC_SUCCESS) { msg = "Cannot read DNIe Version EF"; data[4]=NULL; res = SC_SUCCESS; /* let function return successfully */ goto get_info_end; } data[4]=calloc(bufferlen+1,sizeof(char)); if ( !data[4] ) { msg = "Cannot allocate memory for DNIe Version data"; res = SC_ERROR_OUT_OF_MEMORY; goto get_info_end; } memcpy(data[4],buffer,bufferlen); /* arriving here means ok */ res = SC_SUCCESS; msg = NULL; get_info_end: sc_file_free(file); file = NULL; if (buffer) { free(buffer); buffer=NULL; bufferlen=0; } if (msg) sc_log(card->ctx, "%s", msg); LOG_FUNC_RETURN(card->ctx, res); } /** * Retrieve serial number (7 bytes) from card. * * This is done by mean of an special APDU command described * in the DNIe Reference Manual * * @param card pointer to card description * @param serial where to store data retrieved * @return SC_SUCCESS if ok; else error code */ static int dnie_get_serialnr(sc_card_t * card, sc_serial_number_t * serial) { int result; sc_apdu_t apdu; u8 rbuf[MAX_RESP_BUFFER_SIZE]; if ((card == NULL) || (card->ctx == NULL) || (serial == NULL)) return SC_ERROR_INVALID_ARGUMENTS; LOG_FUNC_CALLED(card->ctx); if (card->type != SC_CARD_TYPE_DNIE_USER) return SC_ERROR_NOT_SUPPORTED; /* if serial number is cached, use it */ if (card->serialnr.len) { memcpy(serial, &card->serialnr, sizeof(*serial)); 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; /* proprietary cmd */ /* send apdu */ result = sc_transmit_apdu(card, &apdu); if (result != SC_SUCCESS) { LOG_TEST_RET(card->ctx, result, "APDU transmit failed"); } if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) return SC_ERROR_INTERNAL; /* cache serial number */ memcpy(card->serialnr.value, apdu.resp, 7 * sizeof(u8)); card->serialnr.len = 7 * sizeof(u8); /* TODO: fill Issuer Identification Number data with proper (ATR?) info */ /* card->serialnr.iin.mii=; card->serialnr.iin.country=; card->serialnr.iin.issuer_id=; */ /* copy and return serial number */ memcpy(serial, &card->serialnr, sizeof(*serial)); sc_log_hex(card->ctx, "Serial Number (apdu)", serial->value, serial->len); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } /** * Remove the binary data in the cache. * * It frees memory if allocated and resets pointer and length. * It only touches the private binary cache variables, not the sc_card information. * * @param data pointer to dnie private data */ static void dnie_clear_cache(dnie_private_data_t * data) { if (data == NULL) return; if (data->cache != NULL) free(data->cache); data->cache = NULL; data->cachelen = 0; } /** * Set sc_card flags according to DNIe requirements. * * Used in card initialization. * * @param card pointer to card data */ static void init_flags(struct sc_card *card) { unsigned long algoflags; /* set up flags according documentation */ card->name = DNIE_CHIP_SHORTNAME; card->cla = 0x00; /* default APDU class (interindustry) */ card->caps |= SC_CARD_CAP_RNG; /* we have a random number generator */ card->max_send_size = (255 - 12); /* manual says 255, but we need 12 extra bytes when encoding */ card->max_recv_size = 255; /* 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); } /**************************** sc_card_operations **********************/ /* Generic operations */ /** * Check if provided card can be handled by OpenDNIe. * * 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. * do not declare static, as used by pkcs15-dnie module * * @param card Pointer to card structure * @return on card matching 0 if not match; negative return means error */ int dnie_match_card(struct sc_card *card) { int result = 0; int matched = -1; LOG_FUNC_CALLED(card->ctx); matched = _sc_match_atr(card, dnie_atrs, &card->type); result = (matched >= 0) ? 1 : 0; LOG_FUNC_RETURN(card->ctx, result); } static int dnie_sm_free_wrapped_apdu(struct sc_card *card, struct sc_apdu *plain, struct sc_apdu **sm_apdu) { struct sc_context *ctx = card->ctx; cwa_provider_t *provider = NULL; int rv = SC_SUCCESS; LOG_FUNC_CALLED(ctx); provider = GET_DNIE_PRIV_DATA(card)->cwa_provider; if (!sm_apdu) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); if (!(*sm_apdu)) LOG_FUNC_RETURN(ctx, SC_SUCCESS); if ((*sm_apdu) != plain) { rv = cwa_decode_response(card, provider, *sm_apdu); 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; } free((unsigned char *) (*sm_apdu)->data); free((*sm_apdu)->resp); free(*sm_apdu); } *sm_apdu = NULL; LOG_FUNC_RETURN(ctx, rv); } 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; LOG_FUNC_CALLED(ctx); if (!plain || !sm_apdu) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); 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) return SC_ERROR_OUT_OF_MEMORY; memcpy(apdu, plain, sizeof(sc_apdu_t)); rv = cwa_encode_apdu(card, provider, plain, apdu); if (rv != SC_SUCCESS) { dnie_sm_free_wrapped_apdu(card, NULL, &apdu); goto err; } *sm_apdu = apdu; } else *sm_apdu = plain; apdu = NULL; err: free(apdu); LOG_FUNC_RETURN(ctx, rv); } /** * OpenDNIe card structures initialization. * * 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. * * @param card Pointer to card structure * @return SC_SUCCES if ok; else error code */ static int dnie_init(struct sc_card *card) { int res = SC_SUCCESS; sc_context_t *ctx = card->ctx; cwa_provider_t *provider = NULL; LOG_FUNC_CALLED(ctx); /* if recognized as terminated DNIe card, return error */ if (card->type == SC_CARD_TYPE_DNIE_TERMINATED) LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_CARD, "DNIe card is terminated."); /* create and initialize cwa-dnie provider*/ provider = dnie_get_cwa_provider(card); if (!provider) LOG_TEST_RET(card->ctx, SC_ERROR_INTERNAL, "Error initializing cwa-dnie provider"); /** Secure messaging initialization section **/ memset(&(card->sm_ctx), 0, sizeof(sm_context_t)); card->sm_ctx.ops.get_sm_apdu = dnie_sm_get_wrapped_apdu; card->sm_ctx.ops.free_sm_apdu = dnie_sm_free_wrapped_apdu; card->sm_ctx.sm_mode = SM_MODE_NONE; res=cwa_create_secure_channel(card,provider,CWA_SM_OFF); LOG_TEST_RET(card->ctx, res, "Failure creating CWA secure channel."); /* initialize private data */ card->drv_data = calloc(1, sizeof(dnie_private_data_t)); if (card->drv_data == NULL) 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); 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); } /** * De-initialization routine. * * Called when the card object is being freed. finish() has to * deallocate all possible private data. * * @param card Pointer to card driver data structure * @return SC_SUCCESS if ok; else error code */ static int dnie_finish(struct sc_card *card) { int result = SC_SUCCESS; LOG_FUNC_CALLED(card->ctx); dnie_clear_cache(GET_DNIE_PRIV_DATA(card)); /* disable sm channel if established */ result = cwa_create_secure_channel(card, GET_DNIE_PRIV_DATA(card)->cwa_provider, CWA_SM_OFF); free(GET_DNIE_PRIV_DATA(card)->cwa_provider); free(card->drv_data); LOG_FUNC_RETURN(card->ctx, result); } /* ISO 7816-4 functions */ /** * Check whether data are compressed. * * @param card pointer to sc_card_t structure * @param from buffer to get data from * @param len buffer length * @return 1 if data are compressed, 0 otherwise; len points to expected length of decompressed data */ static int dnie_is_compressed(sc_card_t * card, u8 * from, size_t len) { #ifdef ENABLE_ZLIB size_t uncompressed = 0L; size_t compressed = 0L; if (!card || !card->ctx || !from || !len) return 0; LOG_FUNC_CALLED(card->ctx); /* 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 = lebytes2ulong(from); compressed = lebytes2ulong(from + 4); /* if compressed size doesn't match data length assume not compressed */ 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."); return 1; compress_exit: #endif sc_log(card->ctx, "Data not compressed."); return 0; } /** * Fill file cache for read_binary() operation. * * Fill a temporary buffer by mean of consecutive calls to read_binary() * until card sends eof * * 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 * 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,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 *p; sc_context_t *ctx = NULL; if (!card || !card->ctx) return SC_ERROR_INVALID_ARGUMENTS; ctx = card->ctx; LOG_FUNC_CALLED(ctx); /* mark cache empty */ dnie_clear_cache(GET_DNIE_PRIV_DATA(card)); /* initialize apdu */ sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xB0, 0x00, 0x00); /* try to read_binary while data available but never long than 32767 */ count = card->max_recv_size; for (len = 0; len < 0x7fff;) { int r = SC_SUCCESS; /* fill apdu */ apdu.p1 = 0xff & (len >> 8); apdu.p2 = 0xff & len; apdu.le = count; apdu.resplen = MAX_RESP_BUFFER_SIZE; apdu.resp = tmp; /* transmit apdu */ r = sc_transmit_apdu(card, &apdu); if (r != SC_SUCCESS) { free(buffer); if (apdu.resp != tmp) free(apdu.resp); sc_log(ctx, "read_binary() APDU transmit failed"); LOG_FUNC_RETURN(ctx, r); } if (apdu.resplen == 0) { /* on no data received, check if requested len is longer than available data in card. If so, ask just for remaining data */ r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r == SC_ERROR_WRONG_LENGTH) { count = 0xff & apdu.sw2; if (count != 0) { if (apdu.resp != tmp) free(apdu.resp); continue; /* read again with correct size */ } goto read_done; /* no more data to read */ } 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 enough space */ count = apdu.resplen; 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); apdu.resp = tmp; } len += count; if (count != card->max_recv_size) goto read_done; } read_done: free((void *)apdu.data); if (apdu.resp != tmp) free(apdu.resp); 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); buffer = p; } /* ok: as final step, set correct cache data into dnie_priv structures */ 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, (int)len); } /** * OpenDNIe implementation of read_binary(). * * Reads a binary stream from card by mean of READ BINARY iso command * Creates and handle a cache to allow data uncompression * * @param card pointer to sc_card_t structure * @param idx offset from card file to ask data for * @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 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) { size_t res = 0; int rc; sc_context_t *ctx = NULL; /* preliminary checks */ if (!card || !card->ctx || !buf || (count <= 0)) return SC_ERROR_INVALID_ARGUMENTS; ctx = card->ctx; LOG_FUNC_CALLED(ctx); if (idx == 0 || GET_DNIE_PRIV_DATA(card)->cache == NULL) { /* on first block or no cache, try to fill */ 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); } } if (idx >= GET_DNIE_PRIV_DATA(card)->cachelen) 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() '%zu' bytes", res); LOG_FUNC_RETURN(ctx, (int)res); } /** * Send apdu to the card * * @param card pointer to sc_card_t structure * @param path * @param pathlen * @param p1 * @param file_out * @return result */ static int dnie_compose_and_send_apdu(sc_card_t *card, const u8 *path, size_t pathlen, u8 p1, sc_file_t **file_out) { int res = 0; sc_apdu_t apdu; u8 rbuf[MAX_RESP_BUFFER_SIZE]; sc_context_t *ctx = NULL; if (!card || !card->ctx) return SC_ERROR_INVALID_ARGUMENTS; ctx = card->ctx; LOG_FUNC_CALLED(ctx); 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) apdu.cse= SC_APDU_CASE_1; if (file_out == NULL) apdu.cse = SC_APDU_CASE_4_SHORT; res = sc_transmit_apdu(card, &apdu); if ((res != SC_SUCCESS) || (file_out == NULL)) LOG_TEST_RET(ctx, res, "SelectFile() APDU transmit failed"); if (file_out == NULL) { if (apdu.sw1 == 0x61) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, 0); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); } /* analyze response. if FCI, try to parse */ res = sc_check_sw(card, apdu.sw1, apdu.sw2); if (res != SC_SUCCESS) { LOG_TEST_RET(ctx, res, "SelectFile() check_sw failed"); } if ((apdu.resplen < 2) || (apdu.resp[0] == 0x00)) { LOG_FUNC_RETURN(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); } 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); } LOG_FUNC_RETURN(ctx, res); } /** * OpenDNIe implementation of Select_File(). * * Select_file: Does the equivalent of SELECT FILE command specified * 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: * -- 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 - 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: * - split SC_PATH_TYPE_PATH into several calls to each 2-byte data file ID * - Translate initial file id 3F00 to be DF name 'Master.File' * * Also, Response always handle a proprietary FCI info, so * need to handle it manually via dnie_process_fci() * * @param card Pointer to Card Structure * @param in_path Path ID to be selected * @param file_out where to store fci information * @return SC_SUCCESS if ok; else error code */ static int dnie_select_file(struct sc_card *card, const struct sc_path *in_path, struct sc_file **file_out) { int res = SC_SUCCESS; sc_context_t *ctx = NULL; unsigned char tmp_path[sizeof(DNIE_MF_NAME)]; size_t reminder = 0; if (!card || !card->ctx || !in_path) return SC_ERROR_INVALID_ARGUMENTS; ctx = card->ctx; LOG_FUNC_CALLED(ctx); switch (in_path->type) { case SC_PATH_TYPE_FILE_ID: /* pathlen must be of len=2 */ /* * 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 * * According iso7816-4 sect 7.1.1 pathlen==0 implies * select MF, but this case is not supported by DNIe */ if (in_path->len != 2) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); 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_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_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 */ LOG_TEST_RET(ctx, res, "sc_lock() failed"); 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_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); LOG_TEST_RET(ctx, res, "select_file(NAME) failed"); } tmp_path[2] = 0; reminder = in_path->len - 2; } else { tmp_path[2] = 0; reminder = in_path->len; } while (reminder > 0) { tmp_path[0] = in_path->value[in_path->len - reminder]; tmp_path[1] = in_path->value[1 + in_path->len - reminder]; sc_log(ctx, "select_file(PATH): requested:%s ", sc_dump_hex(tmp_path, 2)); res = dnie_compose_and_send_apdu(card, tmp_path, 2, 0, file_out); if (res != SC_SUCCESS) { sc_unlock(card); LOG_TEST_RET(ctx, res, "select_file(PATH) failed"); } reminder -= 2; } sc_unlock(card); break; case SC_PATH_TYPE_FROM_CURRENT: LOG_FUNC_RETURN(ctx, SC_ERROR_NO_CARD_SUPPORT); break; case SC_PATH_TYPE_PARENT: /* Hey!! Manual doesn't says anything on this, but * gscriptor shows that this type is supported */ sc_log(ctx, "select_file(PARENT)"); /* according iso7816-4 sect 7.1.1 shouldn't have any parameters */ if (in_path->len != 0) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); res = dnie_compose_and_send_apdu(card, NULL, 0, 3, file_out); break; default: LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); break; } /* as last step clear data cache and return */ dnie_clear_cache(GET_DNIE_PRIV_DATA(card)); LOG_FUNC_RETURN(ctx, res); } /** * OpenDNIe implementation of Get_Challenge() command. * * Get challenge: retrieve 8 random bytes for any further use * (eg perform an external authenticate command) * * 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 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 */ static int dnie_get_challenge(struct sc_card *card, u8 * rnd, size_t len) { /* 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); 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); } /* * ISO 7816-8 functions */ /** * OpenDNIe implementation of Logout() card_driver function. * * Resets all access rights that were gained. Disable SM * * @param card Pointer to Card Structure * @return SC_SUCCESS if OK; else error code */ 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; 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, 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); } /** * Implementation of Set_Security_Environment card driver command. * * 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 * provided for this command; so special apdu is used in cwa code * * @param card Pointer to card driver Structure * @param env Pointer to security environment data * @param num: which Card Security environment to use (ignored in OpenDNIe) * @return SC_SUCCESS if OK; else error code * * TODO: mix these code with SM set_security_env operations * */ static int dnie_set_security_env(struct sc_card *card, const struct sc_security_env *env, int se_num) { sc_apdu_t apdu; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; /* buffer to compose apdu data */ u8 rbuf[MAX_RESP_BUFFER_SIZE]; u8 *p = sbuf; int result = SC_SUCCESS; if ((card == NULL) || (card->ctx == NULL) || (env == NULL)) return SC_ERROR_INVALID_ARGUMENTS; LOG_FUNC_CALLED(card->ctx); if (se_num!=0) { sc_log(card->ctx,"DNIe cannot handle several security envs"); LOG_FUNC_RETURN(card->ctx,SC_ERROR_INVALID_ARGUMENTS); } /* Secure Channel should be on here, if not means an error */ /* result = cwa_create_secure_channel(card, dnie_priv.provider, CWA_SM_WARM); LOG_TEST_RET(card->ctx, result, "set_security_env(); Cannot establish SM"); */ /* check for algorithms */ if (env->flags & SC_SEC_ENV_ALG_REF_PRESENT) { sc_log(card->ctx, "checking algorithms"); switch (env->algorithm) { case SC_ALGORITHM_RSA: result = SC_SUCCESS; break; case SC_ALGORITHM_EC: case SC_ALGORITHM_GOSTR3410: default: result = SC_ERROR_NOT_SUPPORTED; break; } LOG_TEST_RET(card->ctx, result, "Unsupported algorithm"); if ((env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA1) == 0) { result = SC_ERROR_NOT_SUPPORTED; /* TODO: * Manual says that only RSA with SHA1 is supported, but found * some docs where states that SHA256 is also handled */ } LOG_TEST_RET(card->ctx, result, "Only RSA with SHA1 is supported"); /* ok: insert algorithm reference into buffer */ *p++ = 0x80; /* algorithm reference tag */ *p++ = 0x01; /* len */ *p++ = env->algorithm_ref & 0xff; /* val */ } /* check for key references */ if (env->flags & SC_SEC_ENV_KEY_REF_PRESENT) { sc_log(card->ctx, "checking key references"); if (env->key_ref_len != 1) { sc_log(card->ctx, "Null or invalid key ID reference"); LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } sc_log(card->ctx, "Using key reference '%s'", sc_dump_hex(env->key_ref, env->key_ref_len)); /* ok: insert key reference into buffer */ /* notice that DNIe uses same key reference for pubk and privk */ /* see cwa14890-2 sect B.1 about Control Reference Template Tags */ *p++ = 0x84; /* TODO: make proper detection of 0x83 /0x84 tag usage */ *p++ = 0x02; /* len */ *p++ = 0x01; /* key ID prefix (MSB byte of keyFile ID) */ memcpy(p, env->key_ref, env->key_ref_len); /* in DNIe key_ref_len=1 */ p += env->key_ref_len; /* store key reference into private data */ GET_DNIE_PRIV_DATA(card)->rsa_key_ref = 0xff & env->key_ref[0]; } /* create and format apdu */ dnie_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x22, 0x00, 0x00, 255, p - sbuf, rbuf, MAX_RESP_BUFFER_SIZE, sbuf, p - sbuf); /* check and perform operation */ switch (env->operation) { case SC_SEC_OPERATION_DECIPHER: /* TODO: Manual is unsure about if (de)cipher() is supported */ apdu.p1 = 0xC1; apdu.p2 = 0xB8; break; case SC_SEC_OPERATION_SIGN: apdu.p1 = 0x41; /* SET; internal operation */ apdu.p2 = 0xB6; /* Template for Digital Signature */ break; default: LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } /* 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 */ /* send composed apdu and parse result */ result = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, result, "Set Security Environment failed"); result = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_FUNC_RETURN(card->ctx, result); } /** * OpenDNIe implementation of Decipher() card driver operation. * * Engages the deciphering operation. Card will use the * security environment set in a call to set_security_env or * restore_security_env. * * Notice that DNIe manual doesn't say anything about crypt/decrypt * operations. So this code is based on ISO standards and still needs * to be checked * * 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 crgram cryptogram to be (de)ciphered * @param crgram_len cryptogram length * @param out where to store result * @param outlen length of result buffer * @return SC_SUCCESS if OK; else error code */ static int dnie_decipher(struct sc_card *card, const u8 * crgram, size_t crgram_len, u8 * out, size_t outlen) { struct sc_apdu apdu; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; u8 sbuf[MAX_RESP_BUFFER_SIZE]; size_t len; int result = SC_SUCCESS; if ((card == NULL) || (card->ctx == NULL)) return SC_ERROR_INVALID_ARGUMENTS; LOG_FUNC_CALLED(card->ctx); if ((crgram == NULL) || (out == NULL) || (crgram_len > 255)) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } /* Secure Channel should be on. Elsewhere an error will be thrown */ /* result = cwa_create_secure_channel(card, dnie_priv.provider, CWA_SM_WARM); LOG_TEST_RET(card->ctx, result, "decipher(); Cannot establish SM"); */ /* Official driver uses an undocumented proprietary APDU * (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, 0x2A, /* INS: 0x2A perform security operation */ 0x80, /* P1: Response is plain value */ 0x86, /* P2: 8x: Padding indicator byte followed by cryptogram */ 256, crgram_len + 1, rbuf, sizeof(rbuf), sbuf, crgram_len + 1 ); sbuf[0] = 0; /* padding indicator byte, 0x00 = No further indication */ memcpy(sbuf + 1, crgram, crgram_len); /* send apdu */ result = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, result, "APDU transmit failed"); /* check response */ result = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(card->ctx, result, "decipher returned error"); /* responde ok: fill result data and return */ len = apdu.resplen > outlen ? outlen : apdu.resplen; memcpy(out, apdu.resp, len); LOG_FUNC_RETURN(card->ctx, result); } /** * OpenDNIe implementation of Compute_Signature() card driver operation. * * 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 * this operation: * * - (plaintext) Hash on plaintext + sign * - (partial hash) Send a externally evaluated pkcs1 hash + sign * - (hash) directly sign a given sha1 hash * * So the code analyze incoming data, decide which method to be used * and applies * * @param card pointer to sc_card_t structure * @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 * @return * - Positive value: Size of data stored in out buffer when no error * - Negative value: error code */ static int dnie_compute_signature(struct sc_card *card, const u8 * data, size_t datalen, u8 * out, size_t outlen) { int result = SC_SUCCESS; size_t result_resplen = 0; struct sc_apdu apdu; u8 rbuf[MAX_RESP_BUFFER_SIZE]; /* to receive sign response */ /* some preliminary checks */ if ((card == NULL) || (card->ctx == NULL)) return SC_ERROR_INVALID_ARGUMENTS; /* OK: start working */ LOG_FUNC_CALLED(card->ctx); /* more checks */ if ((data == NULL) || (out == NULL)) 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); #ifdef ENABLE_DNIE_UI /* (Requested by DGP): on signature operation, ask user consent */ if (GET_DNIE_PRIV_DATA(card)->rsa_key_ref == 0x02) { /* TODO: revise key ID handling */ result = dnie_ask_user_consent(card,user_consent_title,user_consent_message); LOG_TEST_RET(card->ctx, result, "User consent denied"); } #endif /* 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_hex(card->ctx, "Compute signature\n============================================================", data, datalen); /*INS: 0x2A PERFORM SECURITY OPERATION * P1: 0x9E Resp: Digital Signature * P2: 0x9A Cmd: Input for Digital Signature */ dnie_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x9E, 0x9A, 256, datalen, rbuf, sizeof(rbuf), data, datalen); /* tell card to compute signature */ result = sc_transmit_apdu(card, &apdu); if (result != SC_SUCCESS) { LOG_TEST_RET(card->ctx, result, "compute_signature() failed"); } /* check response */ result = sc_check_sw(card, apdu.sw1, apdu.sw2); if (result != SC_SUCCESS) { LOG_TEST_RET(card->ctx, result, "compute_signature() response error"); } /* 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, (int)result_resplen); } /* * ISO 7816-9 functions */ /** * OpenDNIe implementation of List_Files() card driver operation. * * List available files in current DF * This is a dirty and trick implementation: * Just try every ID in current dir * * @param card Pointer to Card Driver structure * @param buff buffer to store result into * @param bufflen size of provided buffer * @return SC_SUCCESS if OK; else error code * * TODO: check for presence of every file ids on a DF is not * practical. Locate a better way to handle, or remove code */ static int dnie_list_files(sc_card_t * card, u8 * buf, size_t buflen) { int res = SC_SUCCESS; int id1 = 0; int id2 = 0; size_t count = 0; u8 data[2]; sc_apdu_t apdu; if (!card || !card->ctx) return SC_ERROR_INVALID_ARGUMENTS; LOG_FUNC_CALLED(card->ctx); if (!buf || (buflen < 2)) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); /* compose select_file(ID) command */ dnie_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0x00, 0x00, 0, 2, NULL, 0, data, 2); /* iterate on every possible ids */ for (id1 = 0; id1 < 256; id1++) { for (id2 = 0; id2 < 256; id2++) { if (count >= (buflen - 2)) { sc_log(card->ctx, "list_files: end of buffer. Listing stopped"); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } /* according iso several ids are not allowed, so check for it */ if ((id1 == 0x3F) && (id2 == 0xFF)) continue; /* generic parent "." DF */ if ((id1 == 0x2F) && (id2 == 0x00)) continue; /* RFU see iso 8.2.1.1 */ if ((id1 == 0x2F) && (id2 == 0x01)) continue; /* RFU */ /* compose and transmit select_file() cmd */ data[0] = (u8) (0xff & id1); data[1] = (u8) (0xff & id2); res = sc_transmit_apdu(card, &apdu); if (res != SC_SUCCESS) { 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) LOG_FUNC_RETURN(card->ctx, res); continue; } /* if file found, process fci to get file type */ sc_log(card->ctx, "Found File ID '%02X%02X'", id1, id2); /* store id into buffer */ *(buf + count++) = data[0]; *(buf + count++) = data[1]; /* TODO: * if found file is a DF go back to parent DF * to continue search */ } } /* arriving here means all done */ LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } /** * Parse APDU results to generate proper error code. * * Traps standard check_sw function to take care on special error codes * for OpenDNIe (mostly related to SM status and operations) * * @param card Pointer to Card driver Structure * @param sw1 SW1 APDU response byte * @param sw2 SW2 APDU response byte * @return SC_SUCCESS if no error; else proper error code */ static int dnie_check_sw(struct sc_card *card, unsigned int sw1, unsigned int sw2) { int res = SC_SUCCESS; int n = 0; if (!card || !card->ctx) return SC_ERROR_INVALID_ARGUMENTS; LOG_FUNC_CALLED(card->ctx); /* check specific dnie errors */ for (n = 0; dnie_errors[n].SWs != 0; n++) { if (dnie_errors[n].SWs == ((sw1 << 8) | sw2)) { sc_log(card->ctx, "%s", dnie_errors[n].errorstr); return dnie_errors[n].errorno; } } /* arriving here means check for supported iso error codes */ res = iso_ops->check_sw(card, sw1, sw2); LOG_FUNC_RETURN(card->ctx, res); } /** * OpenDNIe implementation for Card_Ctl() card driver operation. * * This command provides access to non standard functions provided by * this card driver, as defined in cardctl.h * * @param card Pointer to card driver structure * @param request Operation requested * @param data where to get data/store response * @return SC_SUCCESS if ok; else error code * @see cardctl.h * * TODO: wait for GET_CARD_INFO generic cardctl to be implemented * in opensc and rewrite code according it */ static int dnie_card_ctl(struct sc_card *card, unsigned long request, void *data) { int result = SC_SUCCESS; if ((card == NULL) || (card->ctx == NULL)) return SC_ERROR_INVALID_ARGUMENTS; LOG_FUNC_CALLED(card->ctx); if (data == NULL) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } switch (request) { /* obtain lifecycle status by reading card->type */ case SC_CARDCTL_LIFECYCLE_GET: switch (card->type) { case SC_CARD_TYPE_DNIE_ADMIN: result = SC_CARDCTRL_LIFECYCLE_ADMIN; break; case SC_CARD_TYPE_DNIE_USER: result = SC_CARDCTRL_LIFECYCLE_USER; break; case SC_CARD_TYPE_DNIE_BLANK: case SC_CARD_TYPE_DNIE_TERMINATED: result = SC_CARDCTRL_LIFECYCLE_OTHER; break; } *(int *)data = result; LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); /* call card to obtain serial number */ case SC_CARDCTL_GET_SERIALNR: result = dnie_get_serialnr(card, (sc_serial_number_t *) data); LOG_FUNC_RETURN(card->ctx, result); case SC_CARDCTL_DNIE_GENERATE_KEY: /* some reports says that this card supports genkey */ result = dnie_generate_key(card, data); LOG_FUNC_RETURN(card->ctx, result); case SC_CARDCTL_DNIE_GET_INFO: /* retrieve name, surname and eid number */ result = dnie_get_info(card, data); LOG_FUNC_RETURN(card->ctx, result); default: /* default: unsupported function */ LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } } /** * Read first bytes of an EF to check for compression data. * * FCI info on compressed files provides the length of the compressed * data. When fci returns filetype = 0x24, needs to check if the * file is compressed, and set up properly correct file length, to let * 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 length. * Lengths are provided as two 4-byte little endian numbers * * Implemented just like a direct read binary apdu bypassing dnie file cache * * @param card sc_card_t structure pointer * @return <0: error code - ==0 not compressed - >0 file size */ static int dnie_read_header(struct sc_card *card) { sc_apdu_t apdu; int r; u8 buf[MAX_RESP_BUFFER_SIZE]; unsigned long uncompressed = 0L; unsigned long compressed = 0L; sc_context_t *ctx = NULL; if (!card || !card->ctx) return SC_ERROR_INVALID_ARGUMENTS; ctx = card->ctx; LOG_FUNC_CALLED(ctx); /* initialize apdu */ dnie_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xB0, 0x00, 0x00, 8, 0, buf, MAX_RESP_BUFFER_SIZE, NULL, 0); /* transmit apdu */ r = sc_transmit_apdu(card, &apdu); if (r != SC_SUCCESS) { sc_log(ctx, "read_header() APDU transmit failed"); LOG_FUNC_RETURN(ctx, r); } /* check response */ if (apdu.resplen != 8) goto header_notcompressed; uncompressed = lebytes2ulong(apdu.resp); compressed = lebytes2ulong(apdu.resp + 4); if (uncompressed < compressed) goto header_notcompressed; if (uncompressed > 32767) goto header_notcompressed; /* ok: assume data is correct */ sc_log(ctx, "read_header: uncompressed file size is %lu", uncompressed); return (int)(0x7FFF & uncompressed); header_notcompressed: sc_log(ctx, "response doesn't match compressed file header"); return 0; } /** * 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 */ static int df_acl[] = { /* to handle DF's */ SC_AC_OP_CREATE, SC_AC_OP_DELETE, SC_AC_OP_REHABILITATE, SC_AC_OP_INVALIDATE, -1 /* !hey!, what about 5th byte of FCI info? */ }; /** * 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 */ static int ef_acl[] = { /* to handle EF's */ SC_AC_OP_READ, SC_AC_OP_UPDATE, SC_AC_OP_REHABILITATE, SC_AC_OP_INVALIDATE, -1 /* !hey!, what about 5th byte of FCI info? */ }; /** * OpenDNIe implementation of Process_FCI() card driver command. * * Parse SelectFile's File Control information. * - First, std iso_parse_fci is called to parse std fci tags * - 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 */ static int dnie_process_fci(struct sc_card *card, struct sc_file *file, const u8 * buf, size_t buflen) { int res = SC_SUCCESS; int *op = df_acl; int n = 0; sc_context_t *ctx = NULL; if ((card == NULL) || (card->ctx == NULL) || (file == NULL) || buflen == 0) return SC_ERROR_INVALID_ARGUMENTS; ctx = card->ctx; LOG_FUNC_CALLED(ctx); /* first of all, let iso do the hard work */ 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 * 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; goto dnie_process_fci_end; } /* at least 10 bytes should be received */ if (file->prop_attr_len < 10) { res = SC_ERROR_WRONG_LENGTH; goto dnie_process_fci_end; } /* byte 0 denotes file type */ switch (file->prop_attr[0]) { case 0x01: /* EF for plain files */ file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = SC_FILE_EF_TRANSPARENT; break; case 0x15: /* EF for keys: linear variable simple TLV */ file->type = SC_FILE_TYPE_WORKING_EF; /* pin file 3F000000 has also this EF type */ if ( ( file->prop_attr[2] == 0x00 ) && (file->prop_attr[3] == 0x00 ) ) { sc_log(ctx,"Processing pin EF"); break; } /* FCI response for Keys EF returns 3 additional bytes */ if (file->prop_attr_len < 13) { sc_log(ctx, "FCI response len for Keys EF should be 13 bytes"); res = SC_ERROR_WRONG_LENGTH; goto dnie_process_fci_end; } break; case 0x24: /* EF for compressed certificates */ file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = SC_FILE_EF_TRANSPARENT; /* evaluate real length by reading first 8 bytes from file */ res = dnie_read_header(card); /* Hey!, we need pin to read certificates... */ if (res == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED) goto dnie_process_fci_end; if (res <= 0) { sc_log(ctx, "Cannot evaluate uncompressed size. use fci length"); } else { sc_log(ctx, "Storing uncompressed size '%d' into fci", res); file->prop_attr[3] = (u8) ((res >> 8) & 0xff); file->prop_attr[4] = (u8) (res & 0xff); } break; case 0x38: /* Errata: manual page 35 says wrong 0x34 */ file->type = SC_FILE_TYPE_DF; break; default: res = SC_ERROR_UNKNOWN_DATA_RECEIVED; goto dnie_process_fci_end; } /* bytes 1 and 2 stores file ID */ 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 ) | ( 0xff & (int)file->prop_attr[4] ) ; /* bytes 5 to 9 states security attributes */ /* 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 * No info available (yet), so copy code from card-jcos.c / card-flex.c * card drivers and pray... */ op = (file->type == SC_FILE_TYPE_DF) ? df_acl : ef_acl; for (n = 0; n < 5; n++) { int key_ref = 0; if (*(op + n) == -1) continue; /* unused entry: skip */ key_ref = file->prop_attr[5 + n] & 0x0F; switch (0xF0 & file->prop_attr[5 + n]) { case 0x00: sc_file_add_acl_entry(file, *(op + n), SC_AC_NONE, SC_AC_KEY_REF_NONE); break; case 0x10: /* this tag is omitted in official code case 0x20: */ case 0x30: sc_file_add_acl_entry(file, *(op + n), SC_AC_CHV, key_ref); break; case 0x40: sc_file_add_acl_entry(file, *(op + n), SC_AC_TERM, key_ref); break; case 0xF0: sc_file_add_acl_entry(file, *(op + n), SC_AC_NEVER, SC_AC_KEY_REF_NONE); break; default: sc_file_add_acl_entry(file, *(op + n), SC_AC_UNKNOWN, SC_AC_KEY_REF_NONE); break; } } /* 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 is 0x15 (EF for keys) */ if (file->prop_attr[0] == 0x15) { 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 RSA crypto files */ /* TODO: write when know what to do */ } res = SC_SUCCESS; /* arriving here means success */ dnie_process_fci_end: LOG_FUNC_RETURN(card->ctx, res); } /* * PIN related functions * NOTE: * DNIe manual says only about CHV1 PIN verify, but several sources talks * about the ability to also handle CHV1 PIN change * So prepare code to eventually support * * Anyway pin unlock is not available: no way to get PUK as these code is * obtained by mean of user fingerprint, only available at police station */ /** * Change PIN. * * Not implemented yet, as current availability for DNIe user driver * is unknown * * @param card Pointer to Card Driver data structure * @param data Pointer to Pin data structure * @return SC_SUCCESS if ok; else error code */ static int dnie_pin_change(struct sc_card *card, struct sc_pin_cmd_data * data) { int res=SC_SUCCESS; LOG_FUNC_CALLED(card->ctx); /* Ensure that secure channel is established from reset */ 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"); LOG_FUNC_RETURN(card->ctx,SC_ERROR_NOT_SUPPORTED); } /** * Verify PIN. * * Initialize SM and send pin verify CHV1 command to DNIe * * @param card Pointer to Card Driver data structure * @param data Pointer to Pin data structure * @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) { int res=SC_SUCCESS; sc_apdu_t apdu; u8 pinbuffer[SC_MAX_APDU_BUFFER_SIZE]; int pinlen = 0; int padding = 0; LOG_FUNC_CALLED(card->ctx); /* ensure that secure channel is established from reset */ if (card->atr.value[15] >= DNIE_30_VERSION) { /* the provider should be prepared for using PIN information */ sc_log(card->ctx, "DNIe 3.0 detected doing PIN initialization"); dnie_change_cwa_provider_to_pin(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"); /* compose pin data to be inserted in apdu */ if (data->flags & SC_PIN_CMD_NEED_PADDING) padding = 1; data->pin1.offset = 0; res = sc_build_pin(pinbuffer, sizeof(pinbuffer), &data->pin1, padding); if (res < 0) LOG_FUNC_RETURN(card->ctx, res); pinlen = res; /* compose apdu */ dnie_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x20, 0x00, 0x00, 0, pinlen, NULL, 0, pinbuffer, pinlen); /* 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); } } res = dnie_check_sw(card, apdu.sw1, apdu.sw2); /* not a pinerr: parse result */ /* ensure that secure channel is established after a PIN channel in 3.0 */ 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); 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); } /* pin_cmd: verify/change/unblock command; optionally using the * card's pin pad if supported. */ /** * OpenDNIe implementation for Pin_Cmd() card driver command. * * @param card Pointer to Card Driver data structure * @param data Pointer to Pin data structure * @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) { int res = SC_SUCCESS; int lc = SC_CARDCTRL_LIFECYCLE_USER; if ((card == NULL) || (card->ctx == NULL) || (data == NULL)) return SC_ERROR_INVALID_ARGUMENTS; LOG_FUNC_CALLED(card->ctx); /* * 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 */ /* ensure that card is in USER Lifecycle */ res = dnie_card_ctl(card, SC_CARDCTL_LIFECYCLE_GET, &lc); LOG_TEST_RET(card->ctx, res, "Cannot get card LC status"); if (lc != SC_CARDCTRL_LIFECYCLE_USER) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_CARD); } /* only allow changes on CHV pin ) */ switch (data->pin_type) { case SC_AC_CHV: /* Card Holder Verifier */ break; case SC_AC_TERM: /* Terminal auth */ case SC_AC_PRO: /* SM auth */ case SC_AC_AUT: /* Key auth */ LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); default: LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } /* This DNIe driver only supports VERIFY operation */ switch (data->cmd) { case SC_PIN_CMD_VERIFY: res = dnie_pin_verify(card,data,tries_left); break; case SC_PIN_CMD_CHANGE: res = dnie_pin_change(card,data); break; case SC_PIN_CMD_UNBLOCK: case SC_PIN_CMD_GET_INFO: res= SC_ERROR_NOT_SUPPORTED; break; default: res= SC_ERROR_INVALID_ARGUMENTS; break; } /* return result */ LOG_FUNC_RETURN(card->ctx, res); } /**********************************************************************/ /** * Internal function to initialize card driver function pointers. * * 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 */ static sc_card_driver_t *get_dnie_driver(void) { sc_card_driver_t *iso_drv = sc_get_iso7816_driver(); /* memcpy() from standard iso7816 declared operations */ if (iso_ops == NULL) iso_ops = iso_drv->ops; dnie_ops = *iso_drv->ops; /* fill card specific function pointers */ /* NULL means that function is not supported neither by DNIe nor iso7816.c */ /* if pointer is omitted, default ISO7816 function will be used */ /* initialization */ dnie_ops.match_card = dnie_match_card; dnie_ops.init = dnie_init; dnie_ops.finish = dnie_finish; /* iso7816-4 functions */ dnie_ops.read_binary = dnie_read_binary; dnie_ops.write_binary = NULL; dnie_ops.update_binary = NULL; dnie_ops.erase_binary = NULL; dnie_ops.read_record = NULL; dnie_ops.write_record = NULL; dnie_ops.append_record = NULL; dnie_ops.update_record = NULL; dnie_ops.select_file = dnie_select_file; dnie_ops.get_challenge = dnie_get_challenge; /* iso7816-8 functions */ dnie_ops.verify = NULL; dnie_ops.logout = dnie_logout; /* dnie_ops.restore_security_env */ dnie_ops.set_security_env = dnie_set_security_env; dnie_ops.decipher = dnie_decipher; dnie_ops.compute_signature = dnie_compute_signature; dnie_ops.change_reference_data = NULL; dnie_ops.reset_retry_counter = NULL; /* iso7816-9 functions */ dnie_ops.create_file = NULL; dnie_ops.delete_file = NULL; dnie_ops.list_files = dnie_list_files; dnie_ops.check_sw = dnie_check_sw; dnie_ops.card_ctl = dnie_card_ctl; dnie_ops.process_fci = dnie_process_fci; /* dnie_ops.construct_fci */ dnie_ops.pin_cmd = dnie_pin_cmd; dnie_ops.get_data = NULL; dnie_ops.put_data = NULL; dnie_ops.delete_record = NULL; return &dnie_driver; } /** * Entry point for (static) OpenDNIe card driver. * * This is the only public function on this module * * @return properly initialized array pointer to card driver operations */ sc_card_driver_t *sc_get_dnie_driver(void) { return get_dnie_driver(); } #undef __CARD_DNIE_C__ #endif /* ENABLE_OPENSSL */ OpenSC-0.26.1/src/libopensc/card-dtrust.c000066400000000000000000000330331474147347300201140ustar00rootroot00000000000000/* * 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 */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "asn1.h" #include "card-cardos-common.h" #include "internal.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 /* internal structure to save the current security environment */ struct dtrust_drv_data_t { 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 }, { NULL, NULL, NULL, 0, 0, NULL } }; // clang-format on // clang-format off static struct dtrust_supported_ec_curves { struct sc_object_id oid; size_t size; } dtrust_curves[] = { { .oid = {{ 1, 2, 840, 10045, 3, 1, 7, -1 }}, .size = 256 }, /* secp256r1 */ { .oid = {{ -1 }}, .size = 0 }, }; // clang-format on 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 (r != 2 || buf[0] != 0xc9 || buf[1] != 0x04) 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 (prodlen != strlen("CardOS V5.4 2019") + 1 || memcmp(buf, "CardOS V5.4 2019", 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 (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; 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_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 (CardOS 5.4)"); return 1; } static int _dtrust_get_serialnr(sc_card_t *card) { int r; u8 buf[32]; r = sc_get_data(card, 0x0181, buf, 32); LOG_TEST_RET(card->ctx, r, "querying serial number failed"); if (r != 8) { sc_log(card->ctx, "unexpected response to GET DATA serial number"); return SC_ERROR_INTERNAL; } /* cache serial number */ memcpy(card->serialnr.value, buf, 8); card->serialnr.len = 8; return SC_SUCCESS; } static int dtrust_init(sc_card_t *card) { 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; card->drv_data = calloc(1, sizeof(struct dtrust_drv_data_t)); if (card->drv_data == NULL) return SC_ERROR_OUT_OF_MEMORY; r = _dtrust_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); flags = 0; r = SC_ERROR_WRONG_CARD; switch (card->type) { case SC_CARD_TYPE_DTRUST_V4_1_STD: case SC_CARD_TYPE_DTRUST_V4_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); r = SC_SUCCESS; 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_ECDH_CDH_RAW; flags |= SC_ALGORITHM_ECDSA_RAW; ext_flags = SC_ALGORITHM_EXT_EC_NAMEDCURVE; for (unsigned int i = 0; dtrust_curves[i].oid.value[0] >= 0; i++) { _sc_card_add_ec_alg(card, dtrust_curves[i].size, flags, ext_flags, &dtrust_curves[i].oid); } r = SC_SUCCESS; break; } LOG_FUNC_RETURN(card->ctx, r); } static int dtrust_finish(sc_card_t *card) { SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); free((char *)card->name); free(card->drv_data); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } 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) { se_num = 0x21; } 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: 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: 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) { sc_path_t path; int r; sc_format_path("3F00", &path); r = sc_select_file(card, &path, NULL); return 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.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; } OpenSC-0.26.1/src/libopensc/card-edo.c000066400000000000000000000244071474147347300173430ustar00rootroot00000000000000/* * 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); struct sc_apdu apdu; sc_format_apdu_ex(&apdu, 00, 0xA4, 0x04, 0x00, name, namelen, 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_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 OpenSC-0.26.1/src/libopensc/card-entersafe.c000066400000000000000000001353741474147347300205560ustar00rootroot00000000000000/* * 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 */ /* Initially written by Weitao Sun (weitao@ftsafe.com) 2008 */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef ENABLE_OPENSSL /* empty file without openssl */ #include #include #include #include "internal.h" #include "asn1.h" #include "cardctl.h" 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 }, { "3b:7c:18:00:00:90:67:46:4a:20:28:8c:58:00:00:00:00", "ff:00:00:00:00:ff:ff:ff:ff:00:00:00:00:ff:ff:ff:ff", "EJAVA/PK-01C-T0",SC_CARD_TYPE_ENTERSAFE_EJAVA_PK_01C_T0,0,NULL}, { "3B:FC:18:00:00:81:31:80:45:90:67:46:4A:21:28:8C:58:00:00:00:00:B7", "ff:00:00:00:00:00:00:00:00:ff:ff:ff:ff:00:00:00:00:ff:ff:ff:ff:00", "EJAVA/H10CR/PK-01C-T1",SC_CARD_TYPE_ENTERSAFE_EJAVA_H10CR_PK_01C_T1,0,NULL}, { "3B:FC:18:00:00:81:31:80:45:90:67:46:4A:20:25:c3:30:00:00:00:00", "ff:00:00:00:00:00:00:00:00:ff:ff:ff:ff:00:00:00:00:00:00:00:00", "EJAVA/D11CR/PK-01C-T1",SC_CARD_TYPE_ENTERSAFE_EJAVA_D11CR_PK_01C_T1,0,NULL}, { "3B:FC:18:00:00:81:31:80:45:90:67:46:4A:00:6A:04:24:00:00:00:00:20", "ff:00:00:00:00:00:00:00:00:ff:ff:ff:ff:00:00:00:00:ff:ff:ff:ff:00", "EJAVA/C21C/PK-01C-T1",SC_CARD_TYPE_ENTERSAFE_EJAVA_C21C_PK_01C_T1,0,NULL}, { "3B:FC:18:00:00:81:31:80:45:90:67:46:4A:00:68:08:04:00:00:00:00:0E", "ff:00:00:00:00:00:00:00:00:ff:ff:ff:ff:00:00:00:00:ff:ff:ff:ff:00", "EJAVA/A22CR/PK-01C-T1",SC_CARD_TYPE_ENTERSAFE_EJAVA_A22CR_PK_01C_T1,0,NULL}, { "3B:FC:18:00:00:81:31:80:45:90:67:46:4A:10:27:61:30:00:00:00:00:0C", "ff:00:00:00:00:00:00:00:00:ff:ff:ff:ff:00:00:00:00:ff:ff:ff:ff:00", "EJAVA/A40CR/PK-01C-T1",SC_CARD_TYPE_ENTERSAFE_EJAVA_A40CR_PK_01C_T1,0,NULL}, { "3b:fc:18:00:00:81:31:80:45:90:67:46:4a:00:68:08:06:00:00:00:00:0c", "FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:00:FF:FF:FF:FF:FF:FF:00:00:00", "FTCOS/PK-01C", SC_CARD_TYPE_ENTERSAFE_FTCOS_PK_01C, 0, NULL }, { NULL, NULL, NULL, 0, 0, NULL } }; static struct sc_card_operations entersafe_ops; static struct sc_card_operations *iso_ops = NULL; static struct sc_card_driver entersafe_drv = { "entersafe", "entersafe", &entersafe_ops, NULL, 0, NULL }; static u8 trans_code_3k[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, }; static u8 trans_code_ftcos_pk_01c[] = { 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, }; static u8 key_maintain[] = { 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) { u8 t; u8 *end = buff + size - 1; while (buff < end) { t = *buff; *buff = *end; *end = t; ++buff; --end; } } static int entersafe_select_file(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file_out); /* the entersafe part */ static int entersafe_match_card(sc_card_t *card) { int i; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); i = _sc_match_atr(card, entersafe_atrs, &card->type); if (i < 0) return 0; return 1; } static int entersafe_init(sc_card_t *card) { unsigned int flags; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); card->name = "entersafe"; card->cla = 0x00; card->drv_data = NULL; 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); 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); } 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; 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); LOG_TEST_RET(card->ctx, r, "entersafe gen random failed"); if (apdu.resplen != size) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); memcpy(buff, rbuf, size); 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; EVP_CIPHER *alg = NULL; u8 iv[8] = {0}; int len; 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_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 = NULL, *tmp_rounded = NULL; size_t tmpsize = 0, tmpsize_rounded = 0; int outl = 0; EVP_CIPHER_CTX *ctx = NULL; EVP_CIPHER *alg = NULL; 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)); 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; } /* 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; } } 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: 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); } static int entersafe_transmit_apdu(sc_card_t *card, sc_apdu_t *apdu, u8 * key, size_t keylen, int cipher, int mac) { 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); assert(card); assert(apdu); 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, 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); out: free(cipher_data); free(mac_data); 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) { sc_apdu_t apdu; u8 recvbuf[SC_MAX_APDU_BUFFER_SIZE]; int r; 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); 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); 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, (int)apdu.resplen); } static int entersafe_update_binary(sc_card_t *card, unsigned int idx, const u8 *buf, size_t count, unsigned long flags) { sc_apdu_t apdu; int r; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); assert(count <= card->max_send_size); 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); 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, (int)count); } static int entersafe_process_fci(struct sc_card *card, struct sc_file *file, 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); 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, unsigned int id_hi, unsigned int id_lo, sc_file_t **file_out) { int r; sc_file_t *file = NULL; sc_path_t path; 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; r = iso_ops->select_file(card, &path, &file); if (r < 0) sc_file_free(file); LOG_TEST_RET(card->ctx, 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; } } if (file_out) *file_out = file; else sc_file_free(file); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); } static int entersafe_select_aid(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file_out) { 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); } 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_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } 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; size_t bMatch = 0; 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; } /* 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 right 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); LOG_TEST_RET(card->ctx, 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_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 = 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) { 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_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) 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) { int r; sc_apdu_t apdu; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); 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; } 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) { int r; sc_apdu_t apdu; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); 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); 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); } static int entersafe_create_ef(sc_card_t *card, sc_entersafe_create_data *data) { int r; sc_apdu_t apdu; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, 0x02, 0x00); apdu.cla = 0x84; 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); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); } static u8 process_acl_entry(sc_file_t *in, unsigned int method, unsigned int in_def) { u8 def = (u8)in_def; const sc_acl_entry_t *entry = sc_file_get_acl_entry(in, method); if (!entry) { return def; } 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) { return ENTERSAFE_AC_NEVER; } 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; } } static int entersafe_internal_set_security_env(sc_card_t *card, const sc_security_env_t *env, u8 ** data,size_t* size) { sc_apdu_t apdu; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; u8 *p = sbuf; int r; 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); } apdu.le = 0; apdu.lc = apdu.datalen = p - sbuf; apdu.data = sbuf; apdu.resplen = 0; r = sc_transmit_apdu(card, &apdu); 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 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 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); SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); 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); 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; } static int entersafe_compute_with_prkey(sc_card_t *card, 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; 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); memcpy(p,data,size); 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"); 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); 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, (int)len); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); } 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); } static int entersafe_decipher(sc_card_t *card, 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); } static void entersafe_init_pin_info(struct sc_pin_cmd_pin *pin, unsigned int num) { pin->encoding = SC_PIN_ENCODING_ASCII; pin->min_length = 4; pin->max_length = 16; pin->pad_length = 16; pin->offset = 5 + num * 16; pin->pad_char = 0x00; } static int entersafe_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left) { 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_log(card->ctx, "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); LOG_TEST_RET(card->ctx, 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); 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) { int r; u8 sbuf[2]; sc_apdu_t apdu; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sbuf[0] = 0x3f; sbuf[1] = 0x00; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 0x00, 0x00); apdu.lc = 2; apdu.datalen = 2; apdu.data = sbuf; r = entersafe_transmit_apdu(card, &apdu, 0, 0, 0, 0); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); sc_invalidate_cache(card); 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; 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; } 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) { 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; } 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_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); 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 */ 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); 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, u8 key_id,u8 usage, u8 factor, sc_pkcs15_bignum_t data) { int r; sc_apdu_t apdu; 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); 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'; */ u8 sbuff[SC_MAX_APDU_BUFFER_SIZE]; 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); /* * 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: { 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); } static int entersafe_write_large_rsa_key(sc_card_t *card,u8 key_id,struct sc_pkcs15_prkey_rsa *rsa) { 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); 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) { 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); 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; 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; 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); } 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] = {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.data = sbuf; apdu.datalen = 4; apdu.lc = 4; apdu.le = 0; 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; sbuf[0] = (u8)(data->key_length >> 8); sbuf[1] = (u8)(data->key_length); apdu.data = sbuf; apdu.lc = 2; apdu.datalen = 2; 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); apdu.cla = 0x80; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = 256; 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 */ 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; 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); } 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); } static int entersafe_get_serialnr(sc_card_t *card, sc_serial_number_t *serial) { int r; sc_apdu_t apdu; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); assert(serial); 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); 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); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); } 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; static u8 const rsa_key_e[] = { 'E', 0x04, 0x01, 0x00, 0x01, 0x00 }; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); /* create rsa item in IKF */ sbuf[0] = 0x04; /* key len extern */ sbuf[1] = 0x0a; /* key len */ sbuf[2] = 0x22; /* USAGE */ sbuf[3] = 0x34; /* user ac */ sbuf[4] = 0x04; /* change ac */ sbuf[5] = 0x34; /* UPDATE AC */ sbuf[6] = 0x40; /* ALGO */ sbuf[7] = 0x00; /* EC */ sbuf[8] = 0x00; /* VER */ memcpy(&sbuf[9], rsa_key_e, sizeof(rsa_key_e)); sbuf[9 + sizeof(rsa_key_e) + 0] = 'C'+'R'+'T'; sbuf[9 + sizeof(rsa_key_e) + 1] = 0x82; 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; 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 */ sbuf[0] = 0x01; /* key len extern */ sbuf[1] = 0x0A; /* key len */ sbuf[2] = 0x2A; /* USAGE */ sbuf[3] = ENTERSAFE_AC_ALWAYS; /* user ac */ sbuf[4] = 0x04; /* change ac */ sbuf[5] = ENTERSAFE_AC_ALWAYS; /* UPDATE AC */ sbuf[6] = 0x40; /* ALGO */ sbuf[7] = 0x00; /* EC */ sbuf[8] = 0x00; /* VER */ 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; 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); } 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; 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); 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) { sc_entersafe_create_data *tmp = (sc_entersafe_create_data *)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; } } 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; entersafe_ops = *iso_drv->ops; entersafe_ops.match_card = entersafe_match_card; 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.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.process_fci = entersafe_process_fci; return &entersafe_drv; } struct sc_card_driver * sc_get_entersafe_driver(void) { return sc_get_driver(); } #endif OpenSC-0.26.1/src/libopensc/card-eoi.c000066400000000000000000000400641474147347300173450ustar00rootroot00000000000000/* * 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 *tries_left) { 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; int 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, tries_left); 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, tries_left); 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, tries_left)); } static int eoi_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) { int r = SC_SUCCESS; LOG_FUNC_CALLED(card->ctx); switch (cmd) { case SC_CARDCTL_GET_MODEL: if (!ptr) r = SC_ERROR_INVALID_ARGUMENTS; else *(char **)ptr = eoi_model; break; default: 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 = (env->algorithm_ref + 7)/8; 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 OpenSC-0.26.1/src/libopensc/card-eoi.h000066400000000000000000000025241474147347300173510ustar00rootroot00000000000000/* * 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]; }; OpenSC-0.26.1/src/libopensc/card-epass2003.c000066400000000000000000002734041474147347300202170ustar00rootroot00000000000000/* * Support for ePass2003 smart cards * * Copyright (C) 2008, Weitao Sun * Copyright (C) 2011, Xiaoshuo Wu * * 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 #ifdef ENABLE_SM /* empty file without SM enabled */ #ifdef ENABLE_OPENSSL /* empty file without openssl */ #include #include #include #include #include #include "internal.h" #include "asn1.h" #include #include #include #include #include #include #include "internal.h" #include "asn1.h" #include "cardctl.h" /* * 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. */ /* 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} }; static struct sc_card_operations *iso_ops = NULL; static struct sc_card_operations epass2003_ops; static struct sc_card_driver epass2003_drv = { "epass2003", "epass2003", &epass2003_ops, NULL, 0, NULL }; #define KEY_TYPE_AES 0x01 /* FIPS mode */ #define KEY_TYPE_DES 0x02 /* Non-FIPS mode */ #define KEY_LEN_AES 16 #define KEY_LEN_DES 8 #define KEY_LEN_DES3 24 #define HASH_LEN 24 static unsigned char PIN_ID[2] = { ENTERSAFE_USER_PIN_ID, ENTERSAFE_SO_PIN_ID }; /*0x00:plain; 0x01:scp01 sm*/ #define SM_PLAIN 0x00 #define SM_SCP01 0x01 static unsigned char g_init_key_enc[16] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10 }; static unsigned char g_init_key_mac[16] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10 }; static unsigned char g_random[8]; typedef struct epass2003_exdata_st { unsigned char sm; /* SM_PLAIN or SM_SCP01 */ unsigned char smtype; /* KEY_TYPE_AES or KEY_TYPE_DES */ 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) ( \ ((unsigned long)x & 0xFF000000)>> 24 | \ ((unsigned long)x & 0x00FF0000)>> 8 | \ ((unsigned long)x & 0x0000FF00)<< 8 | \ ((unsigned long)x & 0x000000FF)<< 24) static const struct sc_card_error epass2003_errors[] = { { 0x6200, SC_ERROR_CARD_CMD_FAILED, "Warning: no information given, non-volatile memory is unchanged" }, { 0x6281, SC_ERROR_CORRUPTED_DATA, "Part of returned data may be corrupted" }, { 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" }, { 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" }, { 0x6581, SC_ERROR_MEMORY_FAILURE, "Memory failure" }, { 0x6700, SC_ERROR_WRONG_LENGTH, "Wrong length" }, { 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" }, { 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" }, { 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" }, { 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" }, { 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"}, { 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" }, { 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 indx; /* 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) { const int err_count = sizeof(epass2003_errors)/sizeof(epass2003_errors[0]); int i; /* Handle special cases here */ if (sw1 == 0x6C) { sc_log(card->ctx, "Wrong length; correct length is %d", sw2); return SC_ERROR_WRONG_LENGTH; } 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; } } sc_log(card->ctx, "Unknown SWs; SW1=%02X, SW2=%02X", sw1, sw2); return SC_ERROR_CARD_CMD_FAILED; } 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)) { epass2003_refresh(card); /* renew old resplen */ apdu->resplen = resplen; r = sc_transmit_apdu(card, apdu); } return r; } static int openssl_enc(const EVP_CIPHER * cipher, const unsigned char *key, const unsigned char *iv, const unsigned char *input, size_t length, unsigned char *output) { int r = SC_ERROR_INTERNAL; EVP_CIPHER_CTX * ctx = NULL; int outl = 0; int outl_tmp = 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; 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)) goto out; r = SC_SUCCESS; out: EVP_CIPHER_CTX_free(ctx); return r; } static int openssl_dec(const EVP_CIPHER * cipher, const unsigned char *key, const unsigned char *iv, const unsigned char *input, size_t length, unsigned char *output) { int r = SC_ERROR_INTERNAL; EVP_CIPHER_CTX * ctx = NULL; int outl = 0; int outl_tmp = 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; 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)) goto out; r = SC_SUCCESS; out: 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_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}; 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(struct sc_card *card, const unsigned char *key, int keysize, unsigned char iv[16], const unsigned char *input, size_t length, unsigned char *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(struct sc_card *card, const unsigned char *key, int keysize, unsigned char iv[16], const unsigned char *input, size_t length, unsigned char *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(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}; 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 { memcpy(&bKey[0], key, 24); } 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(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}; 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 { memcpy(&bKey[0], key, 24); } 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(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}; 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 { memcpy(&bKey[0], key, 24); } 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(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) { 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(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) { 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; } static int openssl_dig(const EVP_MD * digest, const unsigned char *input, size_t length, unsigned char *output) { int r = 0; EVP_MD_CTX *ctx = NULL; unsigned outl = 0; ctx = EVP_MD_CTX_create(); if (ctx == NULL) { r = SC_ERROR_OUT_OF_MEMORY; goto err; } EVP_MD_CTX_init(ctx); if (!EVP_DigestInit_ex(ctx, digest, NULL) || !EVP_DigestUpdate(ctx, input, length)) { r = SC_ERROR_INTERNAL; goto err; } if (!EVP_DigestFinal_ex(ctx, output, &outl)) { r = SC_ERROR_INTERNAL; goto err; } r = SC_SUCCESS; err: if (ctx) EVP_MD_CTX_destroy(ctx); return r; } static int 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) { 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; } static int gen_init_key(struct sc_card *card, unsigned char *key_enc, unsigned char *key_mac, unsigned char *result, unsigned char key_type) { int r; struct sc_apdu apdu; 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}; epass2003_exdata *exdata = NULL; 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 */ 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; exdata->sm = SM_PLAIN; r = epass2003_transmit_apdu(card, &apdu); exdata->sm = tmp_sm; LOG_TEST_RET(card->ctx, r, "APDU gen_init_key failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(card->ctx, r, "gen_init_key failed"); /* Step 1 - Generate Derivation data */ 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) { 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"); } 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) { 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); } 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); } static int verify_init_key(struct sc_card *card, unsigned char *ran_key, unsigned char key_type) { 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 long i; unsigned char tmp_sm; unsigned char isFips; epass2003_exdata *exdata = NULL; if (!card->drv_data) return SC_ERROR_INVALID_ARGUMENTS; exdata = (epass2003_exdata *)card->drv_data; isFips = exdata->bFipsCertification; LOG_FUNC_CALLED(card->ctx); 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) { 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); } LOG_TEST_RET(card->ctx, r, "calculate host cryptogram failed"); memset(data, 0, sizeof(data)); memcpy(data, "\x84\x82\x03\x00\x10", 5); 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) { 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 { 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 */ 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; apdu.lc = apdu.datalen = 16; apdu.data = data; tmp_sm = exdata->sm; 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"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(card->ctx, r, "verify_init_key failed"); return r; } static int mutual_auth(struct sc_card *card, unsigned char *key_enc, unsigned char *key_mac) { struct sc_context *ctx = card->ctx; int r; unsigned char result[256] = {0}; unsigned char ran_key[8] = {0}; epass2003_exdata *exdata = NULL; if (!card->drv_data) return SC_ERROR_INVALID_ARGUMENTS; exdata = (epass2003_exdata *)card->drv_data; LOG_FUNC_CALLED(ctx); r = gen_init_key(card, key_enc, key_mac, result, exdata->smtype); LOG_TEST_RET(ctx, r, "gen_init_key failed"); 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"); LOG_FUNC_RETURN(ctx, r); } int epass2003_refresh(struct sc_card *card) { int r = SC_SUCCESS; epass2003_exdata *exdata = NULL; if (!card->drv_data) return SC_ERROR_INVALID_ARGUMENTS; exdata = (epass2003_exdata *)card->drv_data; if (exdata->sm) { card->sm_ctx.sm_mode = 0; r = mutual_auth(card, g_init_key_enc, g_init_key_mac); card->sm_ctx.sm_mode = SM_MODE_TRANSMIT; LOG_TEST_RET(card->ctx, r, "mutual_auth failed"); } return r; } /* Data(TLV)=0x87|L|0x01+Cipher */ static int construct_data_tlv(struct sc_card *card, struct sc_apdu *apdu, unsigned char *apdu_buf, 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}; size_t pad_len; size_t tlv_more; /* increased tlv length */ unsigned char iv[16] = {0}; epass2003_exdata *exdata = NULL; int r = 0; if (!card->drv_data) return SC_ERROR_INVALID_ARGUMENTS; exdata = (epass2003_exdata *)card->drv_data; /* padding */ apdu_buf[block_size] = 0x87; memcpy(pad, apdu->data, apdu->lc); pad[apdu->lc] = 0x80; if ((apdu->lc + 1) % block_size) pad_len = ((apdu->lc + 1) / block_size + 1) * block_size; else pad_len = apdu->lc + 1; /* encode Lc' */ if (pad_len > 0x7E) { /* Lc' > 0x7E, use extended APDU */ apdu_buf[block_size + 1] = 0x82; apdu_buf[block_size + 2] = (unsigned char)((pad_len + 1) / 0x100); apdu_buf[block_size + 3] = (unsigned char)((pad_len + 1) % 0x100); apdu_buf[block_size + 4] = 0x01; tlv_more = 5; } else { apdu_buf[block_size + 1] = (unsigned char)pad_len + 1; apdu_buf[block_size + 2] = 0x01; tlv_more = 3; } memcpy(data_tlv, &apdu_buf[block_size], tlv_more); /* encrypt Data */ 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; return 0; } /* Le(TLV)=0x97|L|Le */ static int construct_le_tlv(struct sc_apdu *apdu, unsigned char *apdu_buf, size_t data_tlv_len, unsigned char *le_tlv, size_t * le_tlv_len, const unsigned char key_type) { size_t block_size = (KEY_TYPE_AES == key_type ? 16 : 8); *(apdu_buf + block_size + data_tlv_len) = 0x97; if (apdu->le > 0x7F) { /* Le' > 0x7E, use extended APDU */ *(apdu_buf + block_size + data_tlv_len + 1) = 2; *(apdu_buf + block_size + data_tlv_len + 2) = (unsigned char)(apdu->le / 0x100); *(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 { *(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); *le_tlv_len = 3; } return 0; } /* MAC(TLV)=0x8e|0x08|MAC */ static int construct_mac_tlv(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) { size_t block_size = (KEY_TYPE_AES == key_type ? 16 : 8); unsigned char mac[4096] = {0}; size_t mac_len; unsigned char icv[16] = {0}; int r ; int i = (KEY_TYPE_AES == key_type ? 15 : 7); epass2003_exdata *exdata = NULL; if (!card->drv_data) return SC_ERROR_INVALID_ARGUMENTS; exdata = (epass2003_exdata *)card->drv_data; 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; } memset((apdu_buf + block_size + data_tlv_len + le_tlv_len + 1), 0, (mac_len - (data_tlv_len + le_tlv_len + 1))); } /* 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) { 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 (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 * CLA INS P1 P2 [Lc] Data [Le] * to * CLA INS P1 P2 Lc' Data' [Le] * where * Data'=Data(TLV)+Le(TLV)+MAC(TLV) */ static int 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}; size_t data_tlv_len = 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}; epass2003_exdata *exdata = NULL; mac_tlv[0] = 0x8E; mac_tlv[1] = 8; /* size_t plain_le = 0; */ if (!card->drv_data) return SC_ERROR_INVALID_ARGUMENTS; exdata = (epass2003_exdata*)card->drv_data; block_size = (KEY_TYPE_DES == exdata->smtype ? 16 : 8); sm->cse = SC_APDU_CASE_4_SHORT; apdu_buf[0] = (unsigned char)plain->cla; apdu_buf[1] = (unsigned char)plain->ins; apdu_buf[2] = (unsigned char)plain->p1; apdu_buf[3] = (unsigned char)plain->p2; /* plain_le = plain->le; */ /* padding */ 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' */ if (plain->lc != 0) if (0 != construct_data_tlv(card, plain, apdu_buf, dataTLV, &data_tlv_len, exdata->smtype)) return -1; if (plain->le != 0 || (plain->le == 0 && plain->resplen != 0)) if (0 != construct_le_tlv(plain, apdu_buf, data_tlv_len, le_tlv, &le_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; if (sm->lc > 0xFF) { sm->cse = SC_APDU_CASE_4_EXT; apdu_buf[4] = (unsigned char)((sm->lc) / 0x10000); apdu_buf[5] = (unsigned char)(((sm->lc) / 0x100) % 0x100); apdu_buf[6] = (unsigned char)((sm->lc) % 0x100); tmp_lc = 3; } else { apdu_buf[4] = (unsigned char)sm->lc; tmp_lc = 1; } 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); memcpy((unsigned char *)sm->data, apdu_buf + 4 + tmp_lc, sm->datalen); *apdu_buf_len = 0; if (4 == le_tlv_len) { sm->cse = SC_APDU_CASE_4_EXT; *(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) { *(apdu_buf + 4 + tmp_lc + sm->lc) = (unsigned char)plain->le; tmp_le = 1; } *apdu_buf_len += 4 + tmp_lc + data_tlv_len + le_tlv_len + mac_tlv_len + tmp_le; /* sm->le = calc_le(plain_le); */ return 0; } 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 */ size_t buf_len = sizeof(buf); epass2003_exdata *exdata = NULL; if (!card->drv_data) return SC_ERROR_INVALID_ARGUMENTS; exdata = (epass2003_exdata *)card->drv_data; LOG_FUNC_CALLED(card->ctx); if (exdata->sm) plain->cla |= 0x0C; sm->cse = plain->cse; sm->cla = plain->cla; sm->ins = plain->ins; sm->p1 = plain->p1; sm->p2 = plain->p2; sm->lc = plain->lc; sm->le = plain->le; sm->control = plain->control; sm->flags = plain->flags; switch (sm->cla & 0x0C) { case 0x00: case 0x04: sm->datalen = plain->datalen; memcpy((void *)sm->data, plain->data, plain->datalen); sm->resplen = plain->resplen; memcpy(sm->resp, plain->resp, plain->resplen); break; case 0x0C: memset(buf, 0, sizeof(buf)); if (0 != encode_apdu(card, plain, sm, buf, &buf_len)) return SC_ERROR_CARD_CMD_FAILED; break; default: return SC_ERROR_INCORRECT_PARAMETERS; } 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 * ResponseData' SW1 SW2 * to * ResponseData SW1 SW2 * where * ResponseData'=Data(TLV)+SW12(TLV)+MAC(TLV) * where * Data(TLV)=0x87|L|Cipher * SW12(TLV)=0x99|0x02|SW1+SW2 * MAC(TLV)=0x8e|0x08|MAC */ static int decrypt_response(struct sc_card *card, unsigned char *in, size_t inlen, unsigned char *out, size_t * out_len) { size_t cipher_len; size_t i; unsigned char iv[16] = {0}; unsigned char plaintext[4096] = {0}; epass2003_exdata *exdata = NULL; if (!card->drv_data) return SC_ERROR_INVALID_ARGUMENTS; exdata = (epass2003_exdata *)card->drv_data; /* no cipher */ if (in[0] == 0x99) return 0; /* parse cipher length */ if (0x01 == in[2] && 0x82 != in[1]) { cipher_len = in[1]; i = 3; } else if (0x01 == in[3] && 0x81 == in[1]) { cipher_len = in[2]; i = 4; } else if (0x01 == in[4] && 0x82 == in[1]) { cipher_len = in[2] * 0x100; cipher_len += in[3]; i = 5; } else { return -1; } if (cipher_len < 2 || i + cipher_len > inlen || cipher_len > sizeof plaintext) return -1; /* decrypt */ if (KEY_TYPE_AES == exdata->smtype) aes128_decrypt_cbc(card, exdata->sk_enc, 16, iv, &in[i], cipher_len - 1, plaintext); else des3_decrypt_cbc(card, exdata->sk_enc, 16, iv, &in[i], cipher_len - 1, plaintext); /* unpadding */ while (0x80 != plaintext[cipher_len - 2] && (cipher_len > 2)) cipher_len--; if (2 == cipher_len || *out_len < cipher_len - 2) return -1; memcpy(out, plaintext, cipher_len - 2); *out_len = cipher_len - 2; return 0; } static int epass2003_sm_unwrap_apdu(struct sc_card *card, struct sc_apdu *sm, struct sc_apdu *plain) { int r; size_t len = 0; epass2003_exdata *exdata = NULL; 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) { len = plain->resplen; if (0 != decrypt_response(card, sm->resp, sm->resplen, plain->resp, &len)) return SC_ERROR_CARD_CMD_FAILED; } else { memcpy(plain->resp, sm->resp, sm->resplen); len = sm->resplen; } } plain->resplen = len; plain->sw1 = sm->sw1; plain->sw2 = sm->sw2; sc_log(card->ctx, "unwrapped APDU: resplen %"SC_FORMAT_LEN_SIZE_T"u, SW %02X%02X", plain->resplen, plain->sw1, plain->sw2); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } static int epass2003_sm_free_wrapped_apdu(struct sc_card *card, struct sc_apdu *plain, struct sc_apdu **sm_apdu) { struct sc_context *ctx = card->ctx; int rv = SC_SUCCESS; LOG_FUNC_CALLED(ctx); if (!sm_apdu) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); if (!(*sm_apdu)) LOG_FUNC_RETURN(ctx, SC_SUCCESS); if (plain) rv = epass2003_sm_unwrap_apdu(card, *sm_apdu, plain); if ((*sm_apdu)->data) { unsigned char * p = (unsigned char *)((*sm_apdu)->data); free(p); } if ((*sm_apdu)->resp) { free((*sm_apdu)->resp); } free(*sm_apdu); *sm_apdu = NULL; LOG_FUNC_RETURN(ctx, rv); } static int epass2003_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; int rv; LOG_FUNC_CALLED(ctx); if (!plain || !sm_apdu) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); *sm_apdu = NULL; //construct new SM apdu from original apdu apdu = calloc(1, sizeof(struct sc_apdu)); if (!apdu) { rv = SC_ERROR_OUT_OF_MEMORY; goto err; } apdu->data = calloc (1, SC_MAX_EXT_APDU_BUFFER_SIZE); if (!apdu->data) { rv = SC_ERROR_OUT_OF_MEMORY; goto err; } apdu->resp = calloc (1, SC_MAX_EXT_APDU_BUFFER_SIZE); if (!apdu->resp) { rv = SC_ERROR_OUT_OF_MEMORY; goto err; } apdu->datalen = SC_MAX_EXT_APDU_BUFFER_SIZE; apdu->resplen = SC_MAX_EXT_APDU_BUFFER_SIZE; rv = epass2003_sm_wrap_apdu(card, plain, apdu); if (rv) { rv = epass2003_sm_free_wrapped_apdu(card, NULL, &apdu); if (rv < 0) goto err; } *sm_apdu = apdu; apdu = NULL; err: if (apdu) { free((unsigned char *) apdu->data); free(apdu->resp); free(apdu); apdu = NULL; } LOG_FUNC_RETURN(ctx, rv); } static int epass2003_transmit_apdu(struct sc_card *card, struct sc_apdu *apdu) { int r; LOG_FUNC_CALLED(card->ctx); r = sc_transmit_apdu_t(card, apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); return r; } static int get_data(struct sc_card *card, unsigned char type, unsigned char *data, size_t datalen) { int r; struct sc_apdu apdu; 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) return SC_ERROR_INVALID_ARGUMENTS; exdata = (epass2003_exdata *)card->drv_data; LOG_FUNC_CALLED(card->ctx); sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0x01, type); apdu.resp = resp; apdu.le = 0; apdu.resplen = resplen; if (0x86 == type) { /* No SM temporarily */ unsigned char tmp_sm = exdata->sm; exdata->sm = SM_PLAIN; r = sc_transmit_apdu(card, &apdu); exdata->sm = tmp_sm; } else { r = sc_transmit_apdu_t(card, &apdu); } LOG_TEST_RET(card->ctx, r, "APDU get_data failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(card->ctx, r, "get_data failed"); memcpy(data, resp, datalen); return r; } /* card driver functions */ static int epass2003_match_card(struct sc_card *card) { int r; LOG_FUNC_CALLED(card->ctx); r = _sc_match_atr(card, epass2003_atrs, &card->type); if (r < 0) return 0; return 1; } static int epass2003_init(struct sc_card *card) { unsigned int flags; 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); card->name = "epass2003"; card->cla = 0x00; exdata = (epass2003_exdata *)calloc(1, sizeof(epass2003_exdata)); if (!exdata) return SC_ERROR_OUT_OF_MEMORY; card->drv_data = exdata; exdata->sm = SM_SCP01; /* decide FIPS/Non-FIPS mode */ 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; else exdata->smtype = KEY_TYPE_DES; if (0x84 == data[14]) { if (0x00 == data[16]) { exdata->sm = SM_PLAIN; } } /* mutual authentication */ card->max_recv_size = 0xD8; card->max_send_size = 0xE8; card->sm_ctx.ops.open = epass2003_refresh; card->sm_ctx.ops.get_sm_apdu = epass2003_sm_get_wrapped_apdu; card->sm_ctx.ops.free_sm_apdu = epass2003_sm_free_wrapped_apdu; /* FIXME (VT): rather then set/unset 'g_sm', better to implement filter for APDUs to be wrapped */ epass2003_refresh(card); card->sm_ctx.sm_mode = SM_MODE_TRANSMIT; 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); //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); } static int epass2003_finish(sc_card_t *card) { epass2003_exdata *exdata = (epass2003_exdata *)card->drv_data; if (exdata) free(exdata); return SC_SUCCESS; } /* COS implement SFI as lower 5 bits of FID, and not allow same SFI at the * same DF, so use hook functions to increase/decrease FID by 0x20 */ static int epass2003_hook_path(struct sc_path *path, int inc) { 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: case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: if (inc) fid_l = fid_l * FID_STEP; else fid_l = fid_l / FID_STEP; path->value[path->len - 1] = fid_l; return 1; default: break; } return 0; } static int epass2003_hook_file(struct sc_file *file, int inc) { int fidl = file->id & 0xff; int fidh = file->id & 0xff00; 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; } 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 pathbuf[SC_MAX_PATH_SIZE], *path = pathbuf; int r; size_t pathlen; sc_file_t *file = NULL; 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; sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0x00, 0x00); switch (in_path->type) { case SC_PATH_TYPE_FILE_ID: apdu.p1 = 0; if (pathlen != 2) return SC_ERROR_INVALID_ARGUMENTS; break; default: LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } apdu.p2 = 0; /* first record, return FCI */ apdu.lc = pathlen; apdu.data = path; apdu.datalen = pathlen; if (file_out != NULL) { apdu.resp = buf; apdu.resplen = sizeof(buf); apdu.le = 0; } 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 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, "\x6f\x16\x82\x02\x11\x00\x83\x02\x29\x00\x85\x02\x08\x00\x86\x08\xff\x90\x90\x90\xff\xff\xff\xff", apdu.resplen); apdu.resp[9] = path[1]; apdu.sw1 = 0x90; apdu.sw2 = 0x00; } else { r = sc_transmit_apdu_t(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); } if (file_out == NULL) { if (apdu.sw1 == 0x61) LOG_FUNC_RETURN(card->ctx, 0); LOG_FUNC_RETURN(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2)); } r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) LOG_FUNC_RETURN(card->ctx, r); if (apdu.resplen < 2) LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); switch (apdu.resp[0]) { case 0x6F: file = sc_file_new(); if (file == NULL) LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); file->path = *in_path; if (card->ops->process_fci == NULL) { sc_file_free(file); LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } if ((size_t) apdu.resp[1] + 2 <= apdu.resplen) card->ops->process_fci(card, file, apdu.resp + 2, apdu.resp[1]); epass2003_hook_file(file, 0); *file_out = file; break; case 0x00: /* proprietary coding */ LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); break; default: LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); } return 0; } static int epass2003_select_fid(struct sc_card *card, unsigned int id_hi, unsigned int id_lo, sc_file_t ** file_out) { int r; sc_file_t *file = NULL; sc_path_t path; memset(&path, 0, sizeof(path)); path.type = SC_PATH_TYPE_FILE_ID; path.value[0] = id_hi; path.value[1] = id_lo; path.len = 2; 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) { *file_out = file; } else { sc_file_free(file); } LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } static int epass2003_select_aid(struct sc_card *card, const sc_path_t * in_path, sc_file_t ** file_out) { 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); } if (file_out) { sc_file_t *file = *file_out; 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; } LOG_FUNC_RETURN(card->ctx, r); } static int epass2003_select_path(struct sc_card *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; size_t bMatch = 0; unsigned int i; int r; if (pathlen % 2 != 0 || pathlen > 6 || pathlen <= 0) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); /* if pathlen == 6 then the first FID must be MF (== 3F00) */ if (pathlen == 6 && (path[0] != 0x3f || path[1] != 0x00)) LOG_FUNC_RETURN(card->ctx, 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; } /* 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 right 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); } } 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) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); return epass2003_select_fid(card, in_path->value[0], in_path->value[1], file_out); case SC_PATH_TYPE_DF_NAME: return epass2003_select_aid(card, in_path, file_out); case SC_PATH_TYPE_PATH: return epass2003_select_path(card, in_path->value, in_path->len, file_out); default: LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } } 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 *p; unsigned short fid = 0; int r, locked = 0; epass2003_exdata *exdata = NULL; 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 */ *p++ = 0x01; *p++ = 0x84; *p++ = 0x81; *p++ = 0x02; fid = 0x2900; fid += (unsigned short)(0x20 * (env->key_ref[0] & 0xff)); *p++ = fid >> 8; *p++ = fid & 0xff; 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"); locked = 1; } if (apdu.datalen != 0) { r = sc_transmit_apdu_t(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) return 0; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0xF2, se_num); r = sc_transmit_apdu_t(card, &apdu); sc_unlock(card); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); err: if (locked) sc_unlock(card); return r; } static int epass2003_restore_security_env(struct sc_card *card, int se_num) { LOG_FUNC_CALLED(card->ctx); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } static int epass2003_decipher(struct sc_card *card, const u8 * data, size_t datalen, u8 * out, size_t outlen) { int r; struct sc_apdu apdu; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE] = {0}; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE] = {0}; epass2003_exdata *exdata = NULL; LOG_FUNC_CALLED(card->ctx); 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"); 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 acl_to_ac_byte(struct sc_card *card, const struct sc_acl_entry *e) { if (e == NULL) return SC_ERROR_OBJECT_NOT_FOUND; switch (e->method) { case SC_AC_NONE: LOG_FUNC_RETURN(card->ctx, EPASS2003_AC_MAC_NOLESS | EPASS2003_AC_EVERYONE); case SC_AC_NEVER: LOG_FUNC_RETURN(card->ctx, EPASS2003_AC_MAC_NOLESS | EPASS2003_AC_NOONE); default: LOG_FUNC_RETURN(card->ctx, EPASS2003_AC_MAC_NOLESS | EPASS2003_AC_USER); } 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 indx) { int i; int found = 0; unsigned int method; unsigned long keyref; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); switch (file->sec_attr[indx]) { 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[indx], indx); 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 (indx == e->indx && 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) { sc_context_t *ctx = card->ctx; size_t taglen, len = buflen; const u8 *tag = NULL, *p = buf; 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]); } tag = sc_asn1_find_tag(ctx, p, len, 0x80, &taglen); if (tag != NULL && taglen > 0 && taglen < 3) { file->size = tag[0]; if (taglen == 2) file->size = (file->size << 8) + tag[1]; sc_log(ctx, " bytes in file: %"SC_FORMAT_LEN_SIZE_T"u", file->size); } 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_log(ctx, " bytes in file: %d", 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; if (byte == 0x38) { type = "DF"; file->type = SC_FILE_TYPE_DF; } else if (0x01 <= byte && byte <= 0x07) { type = "working EF"; file->type = SC_FILE_TYPE_WORKING_EF; switch (byte) { case 0x01: file->ef_structure = SC_FILE_EF_TRANSPARENT; break; case 0x02: file->ef_structure = SC_FILE_EF_LINEAR_FIXED; break; case 0x04: file->ef_structure = SC_FILE_EF_LINEAR_FIXED; break; default: break; } } else if (0x10 == byte) { type = "BSO"; file->type = SC_FILE_TYPE_BSO; } else if (0x11 <= byte) { type = "internal EF"; file->type = SC_FILE_TYPE_INTERNAL_EF; switch (byte) { case 0x11: break; case 0x12: break; default: break; } } else { type = "unknown"; file->type = SC_FILE_TYPE_INTERNAL_EF; } sc_log(ctx, "type %s, EF structure %d", type, byte); } } 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; sc_log_hex(ctx, "File name", file->name, file->namelen); if (!file->type) file->type = SC_FILE_TYPE_DF; } 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) { 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) { 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; } file->magic = SC_FILE_MAGIC; return 0; } static int epass2003_construct_fci(struct sc_card *card, const sc_file_t * file, u8 * out, size_t * outlen) { u8 *p = out; u8 buf[64]; unsigned char ops[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; int rv; unsigned ii; if (*outlen < 2) return SC_ERROR_BUFFER_TOO_SMALL; *p++ = 0x62; p++; if (file->type == SC_FILE_TYPE_WORKING_EF) { if (file->ef_structure == SC_FILE_EF_TRANSPARENT) { buf[0] = (file->size >> 8) & 0xFF; buf[1] = file->size & 0xFF; sc_asn1_put_tag(0x80, buf, 2, p, *outlen - (p - out), &p); } } if (file->type == SC_FILE_TYPE_DF) { buf[0] = 0x38; buf[1] = 0x00; sc_asn1_put_tag(0x82, buf, 2, p, *outlen - (p - out), &p); } else if (file->type == SC_FILE_TYPE_WORKING_EF) { buf[0] = file->ef_structure & 7; 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) { 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 { return SC_ERROR_NOT_SUPPORTED; } } 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) { buf[0] = 0x11; buf[1] = 0x00; } 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 { 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) { buf[0] = 0x10; buf[1] = 0x00; sc_asn1_put_tag(0x82, 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); 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 { return SC_ERROR_INVALID_ARGUMENTS; } } if (file->type == SC_FILE_TYPE_DF) { 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) { 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) { 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_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); } } 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 { 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) { 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) { 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 { return SC_ERROR_NOT_SUPPORTED; } } 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 || 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 || 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 { return SC_ERROR_NOT_SUPPORTED; } for (ii = 0; ii < sizeof(ops); ii++) { const struct sc_acl_entry *entry; buf[ii] = 0xFF; if (ops[ii] == 0xFF) continue; entry = sc_file_get_acl_entry(file, ops[ii]); rv = acl_to_ac_byte(card, entry); LOG_TEST_RET(card->ctx, rv, "Invalid ACL"); 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 || 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; *outlen = p - out; return 0; } static int epass2003_create_file(struct sc_card *card, sc_file_t * file) { int r; size_t len; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE] = {0}; struct sc_apdu apdu; len = SC_MAX_APDU_BUFFER_SIZE; epass2003_hook_file(file, 1); if (card->ops->construct_fci == NULL) LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); r = epass2003_construct_fci(card, file, sbuf, &len); LOG_TEST_RET(card->ctx, r, "construct_fci() failed"); 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_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, "APDU sw1/2 wrong"); epass2003_hook_file(file, 0); return r; } static int epass2003_delete_file(struct sc_card *card, const sc_path_t * path) { int r; u8 sbuf[2]; struct sc_apdu apdu; LOG_FUNC_CALLED(card->ctx); r = sc_select_file(card, path, NULL); 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"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(card->ctx, r, "Delete file failed"); LOG_FUNC_RETURN(card->ctx, r); } 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}; int r; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x34, 0x00, 0x00); apdu.cla = 0x80; apdu.le = 0; apdu.resplen = sizeof(rbuf); apdu.resp = rbuf; 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, "Card returned error"); if (apdu.resplen == 0x100 && rbuf[0] == 0 && rbuf[1] == 0) LOG_FUNC_RETURN(card->ctx, 0); buflen = buflen < apdu.resplen ? buflen : apdu.resplen; memcpy(buf, rbuf, buflen); LOG_FUNC_RETURN(card->ctx, (int)buflen); } static int internal_write_rsa_key_factor(struct sc_card *card, unsigned short fid, u8 factor, sc_pkcs15_bignum_t data) { int r; struct sc_apdu apdu; u8 sbuff[SC_MAX_EXT_APDU_BUFFER_SIZE] = {0}; LOG_FUNC_CALLED(card->ctx); sbuff[0] = ((fid & 0xff00) >> 8); sbuff[1] = (fid & 0x00ff); memcpy(&sbuff[2], data.data, data.len); // sc_mem_reverse(&sbuff[2], data.len); sc_format_apdu(card, &apdu, SC_APDU_CASE_3, 0xe7, factor, 0x00); apdu.cla = 0x80; apdu.lc = apdu.datalen = 2 + data.len; apdu.data = sbuff; 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, "Write rsa key factor failed"); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } static int internal_write_rsa_key(struct sc_card *card, unsigned short fid, struct sc_pkcs15_prkey_rsa *rsa) { int r; LOG_FUNC_CALLED(card->ctx); r = internal_write_rsa_key_factor(card, fid, 0x02, rsa->modulus); LOG_TEST_RET(card->ctx, r, "write n failed"); r = internal_write_rsa_key_factor(card, fid, 0x03, rsa->d); LOG_TEST_RET(card->ctx, r, "write d failed"); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } static int hash_data(struct sc_card *card, const unsigned char *data, size_t datalen, unsigned char *hash, unsigned int mechanismType) { if ((NULL == data) || (NULL == hash)) return SC_ERROR_INVALID_ARGUMENTS; 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; } static int install_secret_key(struct sc_card *card, unsigned char ktype, unsigned char kid, unsigned char useac, unsigned char modifyac, unsigned char EC, unsigned char *data, unsigned long dataLen) { int r; struct sc_apdu apdu; unsigned char isapp = 0x00; /* appendable */ unsigned char tmp_data[256] = {0}; tmp_data[0] = ktype; tmp_data[1] = kid; tmp_data[2] = useac; tmp_data[3] = modifyac; tmp_data[8] = 0xFF; if (0x04 == ktype || 0x06 == ktype) { tmp_data[4] = EPASS2003_AC_MAC_NOLESS | EPASS2003_AC_SO; tmp_data[5] = EPASS2003_AC_MAC_NOLESS | EPASS2003_AC_SO; tmp_data[7] = (kid == PIN_ID[0] ? EPASS2003_AC_USER : EPASS2003_AC_SO); tmp_data[9] = (EC << 4) | EC; } memcpy(&tmp_data[10], data, dataLen); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xe3, isapp, 0x00); apdu.cla = 0x80; apdu.lc = apdu.datalen = 10 + dataLen; apdu.data = tmp_data; r = sc_transmit_apdu_t(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU install_secret_key failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(card->ctx, r, "install_secret_key failed"); return r; } static int internal_install_pre(struct sc_card *card) { int r; /* init key for enc */ r = install_secret_key(card, 0x01, 0x00, EPASS2003_AC_MAC_NOLESS | EPASS2003_AC_EVERYONE, EPASS2003_AC_MAC_NOLESS | EPASS2003_AC_EVERYONE, 0, g_init_key_enc, 16); LOG_TEST_RET(card->ctx, r, "Install init key failed"); /* init key for mac */ r = install_secret_key(card, 0x02, 0x00, EPASS2003_AC_MAC_NOLESS | EPASS2003_AC_EVERYONE, EPASS2003_AC_MAC_NOLESS | EPASS2003_AC_EVERYONE, 0, g_init_key_mac, 16); LOG_TEST_RET(card->ctx, r, "Install init key failed"); return r; } /* use external auth secret as pin */ static int internal_install_pin(struct sc_card *card, sc_epass2003_wkey_data * pin) { int r; unsigned char hash[HASH_LEN] = {0}; 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, pin->key_data.es_secret.ac[0], pin->key_data.es_secret.ac[1], pin->key_data.es_secret.EC, hash, HASH_LEN); LOG_TEST_RET(card->ctx, r, "Install failed"); return r; } static int epass2003_write_key(struct sc_card *card, sc_epass2003_wkey_data * data) { LOG_FUNC_CALLED(card->ctx); if (data->type & SC_EPASS2003_KEY) { if (data->type == SC_EPASS2003_KEY_RSA) return internal_write_rsa_key(card, data->key_data.es_key.fid, data->key_data.es_key.rsa); else LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } else if (data->type & SC_EPASS2003_SECRET) { if (data->type == SC_EPASS2003_SECRET_PRE) return internal_install_pre(card); else if (data->type == SC_EPASS2003_SECRET_PIN) return internal_install_pin(card, data); else LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } else { LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } static int 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}; LOG_FUNC_CALLED(card->ctx); 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); sbuf[4] = (u8) ((data->prkey_id) & 0xFF); sbuf[5] = (u8) ((data->pukey_id >> 8) & 0xFF); sbuf[6] = (u8) ((data->pukey_id) & 0xFF); /* generate key */ sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x46, 0x00, 0x00); apdu.lc = apdu.datalen = 7; apdu.data = sbuf; 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 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]; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = 0x00; 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, "get pukey failed"); if (len < apdu.resplen) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); 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); } 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); } 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] = "1234567812345678"; static const unsigned char mf_path[2] = { 0x3f, 0x00 }; sc_apdu_t apdu; int r; LOG_FUNC_CALLED(card->ctx); sc_invalidate_cache(card); /* 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); } static int epass2003_get_serialnr(struct sc_card *card, sc_serial_number_t * serial) { u8 rbuf[8]; size_t rbuf_len = sizeof(rbuf); LOG_FUNC_CALLED(card->ctx); if (SC_SUCCESS != get_data(card, 0x80, rbuf, rbuf_len)) return SC_ERROR_CARD_CMD_FAILED; card->serialnr.len = serial->len = 8; memcpy(card->serialnr.value, rbuf, 8); memcpy(serial->value, rbuf, 8); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } static int 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); case SC_CARDCTL_ENTERSAFE_GENERATE_KEY: return epass2003_gen_key(card, (sc_epass2003_gen_key_data *) ptr); case SC_CARDCTL_ERASE_CARD: return epass2003_erase_card(card); case SC_CARDCTL_GET_SERIALNR: return epass2003_get_serialnr(card, (sc_serial_number_t *) ptr); default: return SC_ERROR_NOT_SUPPORTED; } } static void internal_sanitize_pin_info(struct sc_pin_cmd_pin *pin, unsigned int num) { pin->encoding = SC_PIN_ENCODING_ASCII; pin->min_length = 4; pin->max_length = 16; pin->pad_length = 16; pin->offset = 5 + num * 16; pin->pad_char = 0x00; } static int get_external_key_maxtries(struct sc_card *card, unsigned char *maxtries) { 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, 0, SC_PATH_TYPE_PATH, {{0}, 0} }; int ret; ret = sc_select_file(card, &file_path, NULL); LOG_TEST_RET(card->ctx, ret, "select max counter file failed"); ret = sc_read_binary(card, 0, maxcounter, 2, 0); LOG_TEST_RET(card->ctx, ret, "read max counter file failed"); *maxtries = maxcounter[0]; return SC_SUCCESS; } static int get_external_key_retries(struct sc_card *card, unsigned char kid, unsigned char *retries) { int r; struct sc_apdu apdu; 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"); sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x82, 0x01, 0x80 | kid); apdu.resp = NULL; apdu.resplen = 0; r = sc_transmit_apdu_t(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU get_external_key_retries failed"); if (retries && ((0x63 == (apdu.sw1 & 0xff)) && (0xC0 == (apdu.sw2 & 0xf0)))) { *retries = (apdu.sw2 & 0x0f); r = SC_SUCCESS; } else { LOG_TEST_RET(card->ctx, r, "get_external_key_retries failed"); r = SC_ERROR_CARD_CMD_FAILED; } return r; } static int epass2003_get_challenge(sc_card_t *card, u8 *rnd, size_t len) { u8 rbuf[16]; 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 external_key_auth(struct sc_card *card, unsigned char kid, unsigned char *data, size_t datalen) { 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}; r = sc_get_challenge(card, random, 8); LOG_TEST_RET(card->ctx, r, "get challenge external_key_auth failed"); r = hash_data(card, data, datalen, hash, SC_ALGORITHM_ECDSA_HASH_SHA1); LOG_TEST_RET(card->ctx, r, "hash data failed"); 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; r = sc_transmit_apdu_t(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU external_key_auth failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(card->ctx, r, "external_key_auth failed"); return r; } static int update_secret_key(struct sc_card *card, unsigned char ktype, unsigned char kid, 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 maxtries = 0; 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); LOG_TEST_RET(card->ctx, r, "get max counter failed"); tmp_data[0] = (maxtries << 4) | maxtries; memcpy(&tmp_data[1], hash, HASH_LEN); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xe5, ktype, kid); apdu.cla = 0x80; apdu.lc = apdu.datalen = 1 + HASH_LEN; apdu.data = tmp_data; r = sc_transmit_apdu_t(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU update_secret_key failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(card->ctx, r, "update_secret_key failed"); return r; } /* use external auth secret as pin */ static int epass2003_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_left) { int r; u8 kid; u8 retries = 0; u8 pin_low = 3; unsigned char maxtries = 0; LOG_FUNC_CALLED(card->ctx); internal_sanitize_pin_info(&data->pin1, 0); 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; } 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 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); 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"); } 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(); if (iso_ops == NULL) iso_ops = iso_drv->ops; epass2003_ops = *iso_ops; epass2003_ops.match_card = epass2003_match_card; epass2003_ops.init = epass2003_init; epass2003_ops.finish = epass2003_finish; epass2003_ops.write_binary = NULL; epass2003_ops.write_record = NULL; epass2003_ops.select_file = epass2003_select_file; epass2003_ops.get_response = NULL; epass2003_ops.restore_security_env = epass2003_restore_security_env; epass2003_ops.set_security_env = epass2003_set_security_env; epass2003_ops.decipher = epass2003_decipher; epass2003_ops.compute_signature = epass2003_decipher; epass2003_ops.create_file = epass2003_create_file; epass2003_ops.delete_file = epass2003_delete_file; epass2003_ops.list_files = epass2003_list_files; epass2003_ops.card_ctl = epass2003_card_ctl; epass2003_ops.process_fci = epass2003_process_fci; epass2003_ops.construct_fci = epass2003_construct_fci; 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; } struct sc_card_driver *sc_get_epass2003_driver(void) { return sc_get_driver(); } #endif /* #ifdef ENABLE_OPENSSL */ #endif /* #ifdef ENABLE_SM */ OpenSC-0.26.1/src/libopensc/card-esteid2018.c000066400000000000000000000271261474147347300203650ustar00rootroot00000000000000/* * 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 PIN1_REF 0x01 #define PIN2_REF 0x85 #define PUK_REF 0x02 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}, {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 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_check_sw(sc_card_t *card, unsigned int sw1, unsigned int sw2) { if (sw1 == 0x6B && sw2 == 0x00) LOG_FUNC_RETURN(card->ctx, SC_ERROR_FILE_END_REACHED); return iso_ops->check_sw(card, sw1, sw2); } static int esteid_select(struct sc_card *card, unsigned char p1, unsigned char id1, unsigned char id2) { struct sc_apdu apdu; unsigned char sbuf[2]; LOG_FUNC_CALLED(card->ctx); // Select EF/DF sbuf[0] = id1; sbuf[1] = id2; sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0xA4, p1, 0x0C); if (id1 != 0x3F && id2 != 0x00) { apdu.cse = SC_APDU_CASE_3_SHORT; apdu.lc = 2; apdu.data = sbuf; apdu.datalen = 2; } apdu.le = 0; apdu.resplen = 0; SC_TRANSMIT_TEST_RET(card, apdu, "SELECT failed"); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } static int esteid_select_file(struct sc_card *card, const struct sc_path *in_path, struct sc_file **file_out) { unsigned char pathbuf[SC_MAX_PATH_SIZE], *path = pathbuf; size_t pathlen; struct sc_file *file = NULL; 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); } memcpy(path, in_path->value, in_path->len); pathlen = in_path->len; while (pathlen >= 2) { if (memcmp(path, "\x3F\x00", 2) == 0) { LOG_TEST_RET(card->ctx, esteid_select(card, 0x00, 0x3F, 0x00), "MF select failed"); } else if (path[0] == 0xAD) { LOG_TEST_RET(card->ctx, esteid_select(card, 0x01, path[0], path[1]), "DF select failed"); } else if (pathlen == 2) { LOG_TEST_RET(card->ctx, esteid_select(card, 0x02, path[0], path[1]), "EF select failed"); if (file_out != NULL) // Just make a dummy file { file = sc_file_new(); if (file == NULL) LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); file->path = *in_path; file->size = 1536; // Dummy size, to be above 1024 *file_out = file; } } path += 2; pathlen -= 2; } LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } // temporary hack, overload 6B00 SW processing static int esteid_read_binary(struct sc_card *card, unsigned int idx, u8 *buf, size_t count, unsigned long *flags) { int r; int (*saved)(struct sc_card *, unsigned int, unsigned int) = card->ops->check_sw; LOG_FUNC_CALLED(card->ctx); card->ops->check_sw = esteid_check_sw; r = iso_ops->read_binary(card, idx, buf, count, flags); card->ops->check_sw = saved; LOG_FUNC_RETURN(card->ctx, r); } static int esteid_set_security_env(sc_card_t *card, const sc_security_env_t *env, int se_num) { struct esteid_priv_data *priv; struct sc_apdu apdu; // XXX: could be const unsigned char cse_crt_aut[] = {0x80, 0x04, 0xFF, 0x20, 0x08, 0x00, 0x84, 0x01, 0x81}; unsigned char cse_crt_sig[] = {0x80, 0x04, 0xFF, 0x15, 0x08, 0x00, 0x84, 0x01, 0x9F}; unsigned char cse_crt_dec[] = {0x80, 0x04, 0xFF, 0x30, 0x04, 0x00, 0x84, 0x01, 0x81}; 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 && env->key_ref[0] == 1) { sc_format_apdu_ex(&apdu, 0x00, 0x22, 0x41, 0xA4, cse_crt_aut, sizeof(cse_crt_aut), NULL, 0); } else if (env->algorithm == SC_ALGORITHM_EC && env->operation == SC_SEC_OPERATION_SIGN && env->key_ref[0] == 2) { 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 && env->key_ref[0] == 1) { sc_format_apdu_ex(&apdu, 0x00, 0x22, 0x41, 0xB8, cse_crt_dec, sizeof(cse_crt_dec), NULL, 0); } else { LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } SC_TRANSMIT_TEST_RET(card, apdu, "SET SECURITY ENV failed"); 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]; 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)); memset(sbuf, 0x00, SIGNATURE_PAYLOAD_SIZE - datalen); datalen = SIGNATURE_PAYLOAD_SIZE; switch (env->key_ref[0]) { case 1: /* authentication key */ sc_format_apdu_ex(&apdu, 0x00, 0x88, 0, 0, sbuf, datalen, out, le); break; default: sc_format_apdu_ex(&apdu, 0x00, 0x2A, 0x9E, 0x9A, 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) { unsigned char get_pin_info[] = {0x4D, 0x08, 0x70, 0x06, 0xBF, 0x81, 0xFF, 0x02, 0xA0, 0x80}; struct sc_apdu apdu; unsigned char 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(card, 0x00, 0x3F, 0x00), "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); } get_pin_info[6] = pin_reference & 0x0F; // mask out local/global 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 *tries_left) { 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 memcpy(&tmp, data, sizeof(struct sc_pin_cmd_data)); tmp.cmd = SC_PIN_CMD_VERIFY; tmp.pin_reference = PUK_REF; tmp.pin2.len = 0; r = iso_ops->pin_cmd(card, &tmp, tries_left); LOG_TEST_RET(card->ctx, r, "VERIFY during unblock failed"); if (data->pin_reference == 0x85) { 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, tries_left); sc_mem_clear(&tmp, sizeof(tmp)); LOG_FUNC_RETURN(card->ctx, r); } LOG_FUNC_RETURN(card->ctx, iso_ops->pin_cmd(card, data, tries_left)); } 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 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.read_binary = esteid_read_binary; 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; } OpenSC-0.26.1/src/libopensc/card-flex.c000066400000000000000000001073701474147347300175330ustar00rootroot00000000000000/* * card-flex.c: Support for Schlumberger cards * * Copyright (C) 2001, 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., 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" #define FLAG_KEYGEN 0x80000000 #define IS_CYBERFLEX(card) (card->type == SC_CARD_TYPE_FLEX_CYBER) 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 }, /* 8k */ { "3B:95:15:40:FF:68:01:02:02:01", NULL, "Cryptoflex 8K", SC_CARD_TYPE_FLEX_CRYPTO, 0, NULL }, /* 8k */ { "3B:95:15:40:FF:68:01:02:02:04", NULL, "Cryptoflex 8K", SC_CARD_TYPE_FLEX_CRYPTO, 0, NULL }, /* 8k */ { "3B:85:40:20:68:01:01:05:01", NULL, "Cryptoflex 8K", SC_CARD_TYPE_FLEX_CRYPTO, 0, NULL }, /* 16k */ { "3B:95:94:40:FF:63:01:01:02:01", NULL, "Cryptoflex 16K", SC_CARD_TYPE_FLEX_CRYPTO, FLAG_KEYGEN, NULL }, /* "16K+SS1" alias Cryptoflex 16 card with Standard Softmask V1 */ /* (taken from Cryptoflex Card Programmers Guide 4.5 Page xviii) */ /* last two bytes can be ignored - version of the softmask */ { "3B:95:15:40:FF:63:01:01:02:01", "FF:FF:FF:FF:FF:FF:FF:FF:00:00", "Cryptoflex 16K", SC_CARD_TYPE_FLEX_CRYPTO, FLAG_KEYGEN, NULL }, /* 32K v4 */ /* "32K+SS1" alias Cryptoflex 32 card with Standard Softmask V1 */ /* (taken from Cryptoflex Card Programmers Guide 4.5 Page xviii) */ /* last two bytes can be ignored - version of the softmask */ { "3B:95:18:40:FF:64:02:01:01:02","FF:FF:FF:FF:FF:FF:FF:FF:00:00", "Cryptoflex 32K v4", SC_CARD_TYPE_FLEX_CRYPTO, FLAG_KEYGEN, NULL }, /* "32K+e-gate" alias Cryptoflex e-gate 32K card */ /* (taken from Cryptoflex Card Programmers Guide 4.5 Page xviii) */ /* last two bytes can be ignored - version of the softmask */ { "3B:95:18:40:FF:62:01:01:00:00", "FF:FF:FF:FF:FF:FF:FF:FF:00:00", "Cryptoflex e-gate 32K", SC_CARD_TYPE_FLEX_CRYPTO, FLAG_KEYGEN, NULL }, /* 32K e-gate */ { "3B:95:18:40:FF:62:01:02:01:04", NULL, "Cryptoflex 32K e-gate", SC_CARD_TYPE_FLEX_CRYPTO, FLAG_KEYGEN, NULL }, /* 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 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 }, /* + full DES option */ { "3B:E2:00:00:40:20:49:05", NULL, "Cryptoflex", SC_CARD_TYPE_FLEX_CRYPTO, 0, NULL }, /* + Key Generation */ { "3B:E2:00:00:40:20:49:07", NULL, "Cryptoflex", SC_CARD_TYPE_FLEX_CRYPTO, FLAG_KEYGEN, NULL }, /* + Key Generation */ { "3B:85:40:20:68:01:01:03:05", NULL, "Cryptoflex", SC_CARD_TYPE_FLEX_CRYPTO, FLAG_KEYGEN, NULL }, /* Multiflex */ /* 3K */ { "3B:02:14:50", NULL, "Multiflex 3K", SC_CARD_TYPE_FLEX_MULTI, 0, NULL }, /* 4K */ { "3B:19:14:55:90:01:02:01:00:05:04:B0", NULL, "Multiflex 4K", SC_CARD_TYPE_FLEX_MULTI, 0, NULL }, /* 8K */ { "3B:32:15:00:06:80", NULL, "Multiflex 8K", SC_CARD_TYPE_FLEX_MULTI, 0, NULL }, /* 8K + full DES option */ { "3B:32:15:00:06:95", NULL, "Multiflex 8K", SC_CARD_TYPE_FLEX_MULTI, 0, NULL }, /* 8K */ { "3B:19:14:59:01:01:0F:01:00:05:08:B0", NULL, "Multiflex 8K", SC_CARD_TYPE_FLEX_MULTI, 0, NULL }, /* 8K */ { "3B:19:14:55:90:01:01:01:00:05:08:B0", NULL, "Multiflex 8K", SC_CARD_TYPE_FLEX_MULTI, 0, NULL }, /* Cyberflex Access */ /* Crypto */ { "3B:16:94:81:10:06:01:81:3F", NULL, "Cyberflex Access", SC_CARD_TYPE_FLEX_CYBER, 0, NULL }, /* Aug. Crypto */ { "3B:16:94:81:10:06:01:81:2F", NULL, "Cyberflex Access", SC_CARD_TYPE_FLEX_CYBER, 0, NULL }, { NULL, NULL, NULL, 0, 0, NULL } }; struct flex_private_data { int rsa_key_ref; /* Support card variations without having to * do the if (card->type ...) thing * all the time */ u8 aak_key_ref; }; #define DRV_DATA(card) ((struct flex_private_data *) (card)->drv_data) static struct sc_card_operations cryptoflex_ops; static struct sc_card_operations cyberflex_ops; static struct sc_card_operations *iso_ops; static struct sc_card_driver cryptoflex_drv = { "Schlumberger Multiflex/Cryptoflex", "flex", &cryptoflex_ops, NULL, 0, NULL }; static struct sc_card_driver cyberflex_drv = { "Schlumberger Cyberflex", "cyberflex", &cyberflex_ops, NULL, 0, NULL }; static int flex_finish(sc_card_t *card) { free(card->drv_data); return 0; } static int cryptoflex_match_card(sc_card_t *card) { int i; i = _sc_match_atr(card, flex_atrs, NULL); if (i < 0) return 0; switch (flex_atrs[i].type) { case SC_CARD_TYPE_FLEX_CRYPTO: case SC_CARD_TYPE_FLEX_MULTI: card->name = flex_atrs[i].name; card->type = flex_atrs[i].type; card->flags = flex_atrs[i].flags; return 1; } return 0; } static int cyberflex_match_card(sc_card_t *card) { int i; i = _sc_match_atr(card, flex_atrs, NULL); if (i < 0) return 0; switch (flex_atrs[i].type) { case SC_CARD_TYPE_FLEX_CYBER: card->name = flex_atrs[i].name; card->type = flex_atrs[i].type; card->flags = flex_atrs[i].flags; return 1; } return 0; } static int flex_init(sc_card_t *card) { struct flex_private_data *data; if (!(data = malloc(sizeof(*data)))) return SC_ERROR_OUT_OF_MEMORY; card->drv_data = data; card->cla = 0xC0; data->aak_key_ref = 1; /* Override Cryptoflex defaults for specific card types */ switch (card->type) { case SC_CARD_TYPE_FLEX_CYBER: card->cla = 0x00; data->aak_key_ref = 0; break; } /* 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) 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); } /* SCardTransmit failed: 8010002f * this can be solved with a small delay. */ msleep(100); /* State that we have an RNG */ card->caps |= SC_CARD_CAP_RNG; return 0; } static void add_acl_entry(sc_card_t *card, sc_file_t *file, unsigned int op, u8 nibble) { struct flex_private_data *prv = DRV_DATA(card); switch (nibble) { case 0: sc_file_add_acl_entry(file, op, SC_AC_NONE, SC_AC_KEY_REF_NONE); break; case 1: sc_file_add_acl_entry(file, op, SC_AC_CHV, 1); break; case 2: sc_file_add_acl_entry(file, op, SC_AC_CHV, 2); break; case 3: sc_file_add_acl_entry(file, op, SC_AC_PRO, SC_AC_KEY_REF_NONE); break; case 4: /* Assume the key is the AAK */ sc_file_add_acl_entry(file, op, SC_AC_AUT, prv->aak_key_ref); break; case 6: sc_file_add_acl_entry(file, op, SC_AC_CHV, 1); sc_file_add_acl_entry(file, op, SC_AC_PRO, SC_AC_KEY_REF_NONE); break; case 7: sc_file_add_acl_entry(file, op, SC_AC_CHV, 2); sc_file_add_acl_entry(file, op, SC_AC_PRO, SC_AC_KEY_REF_NONE); break; case 8: sc_file_add_acl_entry(file, op, SC_AC_CHV, 1); /* Assume the key is the AAK */ sc_file_add_acl_entry(file, op, SC_AC_AUT, prv->aak_key_ref); break; case 9: sc_file_add_acl_entry(file, op, SC_AC_CHV, 2); /* Assume the key is the AAK */ sc_file_add_acl_entry(file, op, SC_AC_AUT, prv->aak_key_ref); break; case 15: sc_file_add_acl_entry(file, op, SC_AC_NEVER, SC_AC_KEY_REF_NONE); break; default: sc_file_add_acl_entry(file, op, SC_AC_UNKNOWN, SC_AC_KEY_REF_NONE); break; } } static int cryptoflex_get_ac_keys(sc_card_t *card, sc_file_t *file) { return 0; } static int cryptoflex_process_file_attrs(sc_card_t *card, sc_file_t *file, const u8 *buf, size_t buflen) { sc_context_t *ctx = card->ctx; const u8 *p = buf + 2; u8 b1, b2; int is_mf = 0; if (buflen < 14) return -1; b1 = *p++; b2 = *p++; file->size = (b1 << 8) + b2; b1 = *p++; b2 = *p++; file->id = (b1 << 8) + b2; if (file->id == 0x3F00) is_mf = 1; switch (*p) { case 0x01: file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = SC_FILE_EF_TRANSPARENT; break; case 0x02: file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = SC_FILE_EF_LINEAR_FIXED; break; case 0x04: file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = SC_FILE_EF_LINEAR_VARIABLE; break; case 0x06: file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = SC_FILE_EF_CYCLIC; break; case 0x38: file->type = SC_FILE_TYPE_DF; break; default: sc_log(ctx, "invalid file type: 0x%02X\n", *p); return SC_ERROR_UNKNOWN_DATA_RECEIVED; } p += 2; if (file->type == SC_FILE_TYPE_DF) { add_acl_entry(card, file, SC_AC_OP_LIST_FILES, (u8)(p[0] >> 4)); add_acl_entry(card, file, SC_AC_OP_DELETE, (u8)(p[1] >> 4)); add_acl_entry(card, file, SC_AC_OP_CREATE, (u8)(p[1] & 0x0F)); } else { /* EF */ add_acl_entry(card, file, SC_AC_OP_READ, (u8)(p[0] >> 4)); switch (file->ef_structure) { case SC_FILE_EF_TRANSPARENT: add_acl_entry(card, file, SC_AC_OP_UPDATE, (u8)(p[0] & 0x0F)); break; case SC_FILE_EF_LINEAR_FIXED: case SC_FILE_EF_LINEAR_VARIABLE: add_acl_entry(card, file, SC_AC_OP_UPDATE, (u8)(p[0] & 0x0F)); break; case SC_FILE_EF_CYCLIC: break; } } if (file->type != SC_FILE_TYPE_DF || is_mf) { add_acl_entry(card, file, SC_AC_OP_REHABILITATE, (u8)(p[2] >> 4)); add_acl_entry(card, file, SC_AC_OP_INVALIDATE, (u8)(p[2] & 0x0F)); } p += 3; if (*p) file->status = SC_FILE_STATUS_ACTIVATED; else file->status = SC_FILE_STATUS_INVALIDATED; return cryptoflex_get_ac_keys(card, file); } static int cyberflex_process_file_attrs(sc_card_t *card, sc_file_t *file, const u8 *buf, size_t buflen) { sc_context_t *ctx = card->ctx; const u8 *p = buf + 2; const u8 *pos; u8 b1, b2; int is_mf = 0; if (buflen < 14) return -1; b1 = *p++; b2 = *p++; file->size = (b1 << 8) + b2; b1 = *p++; b2 = *p++; file->id = (b1 << 8) + b2; switch (*p) { case 0x01: is_mf = 1; break; case 0x02: file->type = SC_FILE_TYPE_DF; break; case 0x04: file->type = SC_FILE_TYPE_WORKING_EF; break; default: sc_log(ctx, "invalid file type: 0x%02X\n", *p); return SC_ERROR_UNKNOWN_DATA_RECEIVED; } if (is_mf) { sc_file_add_acl_entry(file, SC_AC_OP_LIST_FILES, SC_AC_AUT, 0); sc_file_add_acl_entry(file, SC_AC_OP_DELETE, SC_AC_AUT, 0); sc_file_add_acl_entry(file, SC_AC_OP_CREATE, SC_AC_AUT, 0); } else { p += 2; if (file->type == SC_FILE_TYPE_DF) { add_acl_entry(card, file, SC_AC_OP_LIST_FILES, (u8)(p[0] >> 4)); add_acl_entry(card, file, SC_AC_OP_DELETE, (u8)(p[1] >> 4)); add_acl_entry(card, file, SC_AC_OP_CREATE, (u8)(p[1] & 0x0F)); } else { /* EF */ add_acl_entry(card, file, SC_AC_OP_READ, (u8)(p[0] >> 4)); } } if (file->type != SC_FILE_TYPE_DF) { add_acl_entry(card, file, SC_AC_OP_REHABILITATE, (u8)(p[2] >> 4)); add_acl_entry(card, file, SC_AC_OP_INVALIDATE, (u8)(p[2] & 0x0F)); } pos = p; p += 3; if (*p++) file->status = SC_FILE_STATUS_ACTIVATED; else file->status = SC_FILE_STATUS_INVALIDATED; p++; if (0 == is_mf) { p++; switch (*p) { case 0x00: file->ef_structure = SC_FILE_EF_TRANSPARENT; break; case 0x01: file->ef_structure = SC_FILE_EF_LINEAR_FIXED; break; case 0x02: file->ef_structure = SC_FILE_EF_LINEAR_VARIABLE; break; case 0x03: file->ef_structure = SC_FILE_EF_CYCLIC; break; case 0x04: break; default: sc_log(ctx, "invalid file type: 0x%02X\n", *p); return SC_ERROR_UNKNOWN_DATA_RECEIVED; } switch (file->ef_structure) { case SC_FILE_EF_TRANSPARENT: add_acl_entry(card, file, SC_AC_OP_UPDATE, (u8)(pos[0] & 0x0F)); break; case SC_FILE_EF_LINEAR_FIXED: case SC_FILE_EF_LINEAR_VARIABLE: add_acl_entry(card, file, SC_AC_OP_UPDATE, (u8)(pos[0] & 0x0F)); break; case SC_FILE_EF_CYCLIC: break; } } 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) { int r; sc_apdu_t apdu; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; sc_file_t *file; 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; apdu.resplen = sizeof(rbuf); apdu.datalen = buflen; apdu.data = buf; apdu.lc = buflen; apdu.le = 252; /* No need to get file information, if file is NULL. */ if (file_out == NULL) { apdu.cse = SC_APDU_CASE_3_SHORT; apdu.le = 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, "Card returned error"); if (file_out == NULL) return 0; if (apdu.resplen < 14) return SC_ERROR_UNKNOWN_DATA_RECEIVED; if (apdu.resp[0] == 0x6F) { sc_log(card->ctx, "unsupported: card returned FCI\n"); return SC_ERROR_UNKNOWN_DATA_RECEIVED; /* FIXME */ } file = sc_file_new(); if (file == NULL) 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); if (r) { sc_file_free(file); return r; } *file_out = file; return 0; } static int flex_select_file(sc_card_t *card, const sc_path_t *path, sc_file_t **file_out) { int r; const u8 *pathptr = path->value; size_t pathlen = path->len; int locked = 0, magic_done; 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_log(card->ctx, "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); LOG_TEST_RET(card->ctx, r, "sc_lock() failed"); if (!magic_done && memcmp(pathptr, "\x3F\x00", 2) != 0) { r = select_file_id(card, (const u8 *) "\x3F\x00", 2, 0, NULL); if (r) sc_unlock(card); 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); LOG_TEST_RET(card->ctx, r, "Unable to select DF"); pathptr += 2; pathlen -= 2; } } break; case SC_PATH_TYPE_DF_NAME: p1 = 0x04; break; case SC_PATH_TYPE_FILE_ID: if (pathlen != 2) return SC_ERROR_INVALID_ARGUMENTS; break; } 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); } static int cryptoflex_list_files(sc_card_t *card, u8 *buf, size_t buflen) { sc_apdu_t apdu; 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; apdu.resplen = 4; apdu.resp = rbuf; while (buflen > 2) { r = sc_transmit_apdu(card, &apdu); if (r) return r; if (apdu.sw1 == 0x6A && apdu.sw2 == 0x82) break; r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) return r; if (apdu.resplen != 4) { sc_log(card->ctx, "expected 4 bytes, got %"SC_FORMAT_LEN_SIZE_T"u.\n", apdu.resplen); return SC_ERROR_UNKNOWN_DATA_RECEIVED; } memcpy(buf, rbuf + 2, 2); buf += 2; count += 2; buflen -= 2; } return (int)count; } /* * The Cyberflex LIST FILES command is slightly different... */ static int cyberflex_list_files(sc_card_t *card, u8 *buf, size_t buflen) { sc_apdu_t apdu; u8 rbuf[6]; 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; apdu.resplen = 6; apdu.resp = rbuf; r = sc_transmit_apdu(card, &apdu); if (r) return r; if (apdu.sw1 == 0x6A && apdu.sw2 == 0x83) break; r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) return r; if (apdu.resplen != 6) { sc_log(card->ctx, "expected 6 bytes, got %"SC_FORMAT_LEN_SIZE_T"u.\n", apdu.resplen); return SC_ERROR_UNKNOWN_DATA_RECEIVED; } memcpy(buf, rbuf + 4, 2); buf += 2; count += 2; buflen -= 2; } return (int)count; } static int flex_delete_file(sc_card_t *card, const sc_path_t *path) { sc_apdu_t apdu; int r; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (path->type != SC_PATH_TYPE_FILE_ID && path->len != 2) { 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)) apdu.cla = 0xF0; /* Override CLA byte */ apdu.data = path->value; apdu.lc = 2; apdu.datalen = 2; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); } 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_CHV: switch (e->key_ref) { case 1: return 0x01; break; case 2: return 0x02; break; } return -1; case SC_AC_PRO: return 0x03; case SC_AC_AUT: return 0x04; case SC_AC_NEVER: return 0x0f; } return -1; } static int acl_to_keynum_nibble(const sc_acl_entry_t *e) { while (e != NULL && e->method != SC_AC_AUT) e = e->next; if (e == NULL || e->key_ref == SC_AC_KEY_REF_NONE) return 0; return e->key_ref & 0x0F; } static int cryptoflex_construct_file_attrs(sc_card_t *card, const sc_file_t *file, u8 *buf, size_t *buflen) { u8 *p = buf; int r, i; int ops[6]; p[0] = 0xFF; p[1] = 0xFF; p[2] = file->size >> 8; p[3] = file->size & 0xFF; p[4] = file->id >> 8; p[5] = file->id & 0xFF; if (file->type == SC_FILE_TYPE_DF) p[6] = 0x38; else switch (file->ef_structure) { case SC_FILE_EF_TRANSPARENT: p[6] = 0x01; break; case SC_FILE_EF_LINEAR_FIXED: p[6] = 0x02; break; case SC_FILE_EF_LINEAR_VARIABLE: p[6] = 0x04; break; case SC_FILE_EF_CYCLIC: p[6] = 0x06; break; default: sc_log(card->ctx, "Invalid EF structure\n"); return -1; } p[7] = 0xFF; /* allow Decrease and Increase */ for (i = 0; i < 6; i++) ops[i] = -1; if (file->type == SC_FILE_TYPE_DF) { ops[0] = SC_AC_OP_LIST_FILES; ops[2] = SC_AC_OP_DELETE; ops[3] = SC_AC_OP_CREATE; } else { ops[0] = SC_AC_OP_READ; ops[1] = SC_AC_OP_UPDATE; ops[2] = SC_AC_OP_READ; ops[3] = SC_AC_OP_UPDATE; ops[4] = SC_AC_OP_REHABILITATE; ops[5] = SC_AC_OP_INVALIDATE; } p[8] = p[9] = p[10] = 0; p[13] = p[14] = p[15] = 0; /* Key numbers */ for (i = 0; i < 6; i++) { const sc_acl_entry_t *entry; if (ops[i] == -1) continue; entry = sc_file_get_acl_entry(file, ops[i]); r = acl_to_ac_nibble(entry); 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; if (file->type != SC_FILE_TYPE_DF && (file->ef_structure == SC_FILE_EF_LINEAR_FIXED || file->ef_structure == SC_FILE_EF_CYCLIC)) p[12] = 0x04; else p[12] = 0x03; if (p[12] == 0x04) { p[16] = file->record_length; *buflen = 17; } else *buflen = 16; return 0; } static int cyberflex_construct_file_attrs(sc_card_t *card, const sc_file_t *file, u8 *buf, size_t *buflen) { u8 *p = buf; size_t size = file->size; /* cyberflex wants input parameters length added */ switch (file->type) { case SC_FILE_TYPE_DF: size += 24; break; case SC_FILE_TYPE_WORKING_EF: default: size += 16; break; } 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, size, size >> 8, size & 0xFF); p[0] = size >> 8; p[1] = size & 0xFF; p[2] = file->id >> 8; p[3] = file->id & 0xFF; if (file->type == SC_FILE_TYPE_DF) p[4] = 0x20; else switch (file->ef_structure) { case SC_FILE_EF_TRANSPARENT: p[4] = 0x02; break; case SC_FILE_EF_LINEAR_FIXED: p[4] = 0x0C; break; case SC_FILE_EF_LINEAR_VARIABLE: p[4] = 0x19; break; case SC_FILE_EF_CYCLIC: p[4] = 0x1D; break; default: 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; p[10] = p[12] = p[13] = p[14] = p[15] = 0x00; return 0; } static int flex_create_file(sc_card_t *card, sc_file_t *file) { u8 sbuf[18]; 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_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 = (int)file->record_count; else rec_nr = 0; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, 0x00, rec_nr); if (!IS_CYBERFLEX(card)) apdu.cla = 0xF0; apdu.data = sbuf; apdu.datalen = sendlen; apdu.lc = sendlen; 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"); 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; } static int flex_set_security_env(sc_card_t *card, const sc_security_env_t *env, 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_log(card->ctx, "Invalid crypto operation supplied.\n"); return SC_ERROR_NOT_SUPPORTED; } if (env->algorithm != SC_ALGORITHM_RSA) { 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_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_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_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_log(card->ctx, "File reference is not 0012.\n"); return SC_ERROR_NOT_SUPPORTED; } return 0; } static int flex_restore_security_env(sc_card_t *card, int se_num) { return 0; } static int cryptoflex_compute_signature(sc_card_t *card, const u8 *data, size_t data_len, u8 * out, size_t outlen) { struct flex_private_data *prv = (struct flex_private_data *) card->drv_data; sc_apdu_t apdu; 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_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_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); /* This works around a problem with some PC/SC IFD drivers that don't grok * lc=00 (Chaskiel M Grundman ) */ if (data_len == 256) { apdu.cla=0x10; apdu.cse= SC_APDU_CASE_3_SHORT; apdu.lc=10; apdu.datalen=10; apdu.data = sbuf; for (i2 = 0; i2 < 10; i2++) sbuf[i2]=data[data_len-1-i2]; 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"); data_len -= 10; sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x88, 0x00, prv->rsa_key_ref); apdu.cla=0x0; } apdu.lc = data_len; apdu.datalen = data_len; for (i = 0; i < data_len; i++) sbuf[i] = data[data_len-1-i]; apdu.data = sbuf; apdu.resplen = outlen > sizeof(sbuf) ? sizeof(sbuf) : outlen; apdu.le = apdu.resplen > 256 ? 256 : apdu.resplen; apdu.resp = sbuf; 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"); for (i = 0; i < apdu.resplen; i++) out[i] = sbuf[apdu.resplen-1-i]; return (int)apdu.resplen; } static int cyberflex_compute_signature(sc_card_t *card, const u8 *data, size_t data_len, u8 * out, size_t outlen) { struct flex_private_data *prv = DRV_DATA(card); 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_log(card->ctx, "Illegal input length: %"SC_FORMAT_LEN_SIZE_T"u\n", data_len); return SC_ERROR_INVALID_ARGUMENTS; } key_id = prv->rsa_key_ref + 1; /* Why? */ if (outlen < data_len) { 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); apdu.lc = data_len; apdu.datalen = data_len; apdu.data = data; apdu.resplen = outlen; apdu.resp = out; 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"); return (int)apdu.resplen; } static int flex_decipher(sc_card_t *card, const u8 * crgram, size_t crgram_len, u8 * out, size_t outlen) { /* There seems to be no Decipher command, but an RSA signature * is the same operation as an RSA decryption. * Of course, the (PKCS#1) padding is different, but at least * a Cryptoflex 32K e-gate doesn't seem to check this. */ return card->ops->compute_signature(card, crgram, crgram_len, out, outlen); } /* Return the default AAK for this type of card */ static int flex_get_default_key(sc_card_t *card, struct sc_cardctl_default_key *data) { struct flex_private_data *prv = DRV_DATA(card); const char *key; if (data->method != SC_AC_AUT || data->key_ref != prv->aak_key_ref) return SC_ERROR_NO_DEFAULT_KEY; /* These seem to be the default AAKs used by Schlumberger */ switch (card->type) { case SC_CARD_TYPE_FLEX_CRYPTO: key = "2c:15:e5:26:e9:3e:8a:19"; break; case SC_CARD_TYPE_FLEX_CYBER: key = "ad:9f:61:fe:fa:20:ce:63"; break; default: return SC_ERROR_NO_DEFAULT_KEY; } return sc_hex_to_bin(key, data->key_data, &data->len); } /* Generate key on-card */ static int flex_generate_key(sc_card_t *card, struct sc_cardctl_cryptoflex_genkey_info *data) { 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_log(card->ctx, "Illegal key length: %zu\n", data->key_bits); return SC_ERROR_INVALID_ARGUMENTS; } p1 = data->key_num; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x46, p1, p2); if (!IS_CYBERFLEX(card)) apdu.cla = 0xF0; apdu.data = sbuf; apdu.datalen = 4; apdu.lc = 4; /* Little endian representation of exponent */ sbuf[0] = data->exponent & 0xFF; sbuf[1] = (data->exponent >> 8) & 0xFF; sbuf[2] = (data->exponent >> 16) & 0xFF; sbuf[3] = (data->exponent >> 24) & 0xFF; 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"); data->pubkey_len = (unsigned)apdu.resplen; return 0; } /* read the card serial number from the EF_gdo system file */ static int flex_get_serialnr(sc_card_t *card, sc_serial_number_t *serial) { int r; u8 buf[16]; size_t len; sc_path_t tpath; sc_file_t *tfile = NULL; 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; } /* read EF_ICCSN */ sc_format_path("3F000002", &tpath); r = sc_select_file(card, &tpath, &tfile); if (r < 0) return r; len = tfile->size; sc_file_free(tfile); if (len != 8) { 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; memcpy(card->serialnr.value, buf, len); memcpy(serial, &card->serialnr, sizeof(*serial)); return SC_SUCCESS; } static int flex_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) { switch (cmd) { case SC_CARDCTL_GET_DEFAULT_KEY: return flex_get_default_key(card, (struct sc_cardctl_default_key *) ptr); case SC_CARDCTL_CRYPTOFLEX_GENERATE_KEY: return flex_generate_key(card, (struct sc_cardctl_cryptoflex_genkey_info *) ptr); case SC_CARDCTL_GET_SERIALNR: return flex_get_serialnr(card, (sc_serial_number_t *) ptr); } return SC_ERROR_NOT_SUPPORTED; } static int flex_build_verify_apdu(sc_card_t *card, sc_apdu_t *apdu, struct sc_pin_cmd_data *data) { static u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; int r, len; int cla = card->cla, ins; switch (data->pin_type) { case SC_AC_CHV: ins = 0x20; break; case SC_AC_AUT: /* AUT keys cannot be entered through terminal */ if (data->flags & SC_PIN_CMD_USE_PINPAD) return SC_ERROR_INVALID_ARGUMENTS; /* Override CLA byte */ if (!IS_CYBERFLEX(card)) cla = 0xF0; ins = 0x2A; break; default: return SC_ERROR_INVALID_ARGUMENTS; } /* Copy the PIN, with padding */ if ((r = sc_build_pin(sbuf, sizeof(sbuf), &data->pin1, 1)) < 0) return r; len = r; sc_format_apdu(card, apdu, SC_APDU_CASE_3_SHORT, ins, 0, data->pin_reference); apdu->cla = cla; apdu->data = sbuf; apdu->datalen = len; apdu->lc = len; return 0; } static void flex_init_pin_info(struct sc_pin_cmd_pin *pin, unsigned int num) { pin->encoding = SC_PIN_ENCODING_ASCII; pin->min_length = 4; pin->max_length = 8; pin->pad_length = 8; pin->offset = 5 + num * 8; } static int flex_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left) { sc_apdu_t apdu; int r; int old_cla = -1; /* Fix pin data */ data->flags |= SC_PIN_CMD_NEED_PADDING; flex_init_pin_info(&data->pin1, 0); flex_init_pin_info(&data->pin2, 1); if (data->cmd == SC_PIN_CMD_VERIFY) { r = flex_build_verify_apdu(card, &apdu, data); if (r < 0) return r; data->apdu = &apdu; } else if (data->cmd == SC_PIN_CMD_CHANGE || data->cmd == SC_PIN_CMD_UNBLOCK) { if (data->pin_type != SC_AC_CHV) return SC_ERROR_INVALID_ARGUMENTS; old_cla = card->cla; if (!IS_CYBERFLEX(card)) card->cla = 0xF0; } /* 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); if (old_cla != -1) card->cla = old_cla; data->apdu = NULL; return r; } static int flex_logout(sc_card_t *card) { sc_apdu_t apdu; int r; sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x22, 0x07, 0x00); apdu.cla = 0xF0; 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); } struct sc_card_driver * sc_get_cryptoflex_driver(void) { if (iso_ops == NULL) iso_ops = sc_get_iso7816_driver()->ops; cryptoflex_ops = *iso_ops; cryptoflex_ops.match_card = cryptoflex_match_card; cryptoflex_ops.init = flex_init; cryptoflex_ops.finish = flex_finish; cryptoflex_ops.process_fci = cryptoflex_process_file_attrs; cryptoflex_ops.construct_fci = cryptoflex_construct_file_attrs; cryptoflex_ops.select_file = flex_select_file; cryptoflex_ops.list_files = cryptoflex_list_files; cryptoflex_ops.delete_file = flex_delete_file; cryptoflex_ops.create_file = flex_create_file; cryptoflex_ops.card_ctl = flex_card_ctl; cryptoflex_ops.set_security_env = flex_set_security_env; cryptoflex_ops.restore_security_env = flex_restore_security_env; cryptoflex_ops.compute_signature = cryptoflex_compute_signature; cryptoflex_ops.decipher = flex_decipher; cryptoflex_ops.pin_cmd = flex_pin_cmd; cryptoflex_ops.logout = flex_logout; return &cryptoflex_drv; } struct sc_card_driver * sc_get_cyberflex_driver(void) { if (iso_ops == NULL) iso_ops = sc_get_iso7816_driver()->ops; cyberflex_ops = *iso_ops; cyberflex_ops.match_card = cyberflex_match_card; cyberflex_ops.init = flex_init; cyberflex_ops.finish = flex_finish; cyberflex_ops.process_fci = cyberflex_process_file_attrs; cyberflex_ops.construct_fci = cyberflex_construct_file_attrs; cyberflex_ops.select_file = flex_select_file; cyberflex_ops.list_files = cyberflex_list_files; cyberflex_ops.delete_file = flex_delete_file; cyberflex_ops.create_file = flex_create_file; cyberflex_ops.card_ctl = flex_card_ctl; cyberflex_ops.set_security_env = flex_set_security_env; cyberflex_ops.restore_security_env = flex_restore_security_env; cyberflex_ops.compute_signature = cyberflex_compute_signature; cyberflex_ops.decipher = flex_decipher; cyberflex_ops.pin_cmd = flex_pin_cmd; cyberflex_ops.logout = flex_logout; return &cyberflex_drv; } OpenSC-0.26.1/src/libopensc/card-gemsafeV1.c000066400000000000000000000446031474147347300204120ustar00rootroot00000000000000/* * 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 */ /* Initially written by David Mattes (david.mattes@boeing.com) */ /* Portuguese eID card support by Joao Poupino (joao.poupino@ist.utl.pt) */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "internal.h" #include "asn1.h" #include "cardctl.h" #define GEMSAFEV1_ALG_REF_FREEFORM 0x12 #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; static struct sc_card_driver gemsafe_drv = { "Gemalto GemSafe V1 applet", "gemsafeV1", &gemsafe_ops, NULL, 0, NULL }; /* Known 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}, /* GemSafeXpresso 32K */ {"3b:6d:00:00:80:31:80:65:b0:83:01:02:90:83:00:90:00", NULL, NULL, SC_CARD_TYPE_GEMSAFEV1_GENERIC, 0, NULL}, /* fips 140 version */ {"3B:6B:00:00:80:65:B0:83:01:03:74:83:00:90:00", NULL, NULL, SC_CARD_TYPE_GEMSAFEV1_GENERIC, 0, NULL}, /* Undefined */ {"3B:7A:94:00:00:80:65:A2:01:01:01:3D:72:D6:43", NULL, NULL, SC_CARD_TYPE_GEMSAFEV1_GENERIC, 0, NULL}, {"3B:7D:94:00:00:80:31:80:65:B0:83:01:01:90:83:00:90:00", NULL, NULL, SC_CARD_TYPE_GEMSAFEV1_GENERIC, 0, NULL}, {"3B:7D:96:00:00:80:31:80:65:B0:83:11:48:C8:83:00:90:00", NULL, NULL, SC_CARD_TYPE_GEMSAFEV1_GENERIC, 0, NULL}, /* Portuguese eID cards */ {"3B:7D:95:00:00:80:31:80:65:B0:83:11:C0:A9:83:00", NULL, NULL, SC_CARD_TYPE_GEMSAFEV1_PTEID, 0, NULL}, {"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*/ {"3b:7d:96:00:00:80:31:80:65:b0:83:02:01:f3:83:00:90:00", NULL, NULL, SC_CARD_TYPE_GEMSAFEV1_SEEID, 0, NULL}, {NULL, NULL, NULL, 0, 0, NULL} }; static const u8 gemsafe_def_aid[] = {0xA0, 0x00, 0x00, 0x00, 0x18, 0x0A, 0x00, 0x00, 0x01, 0x63, 0x42, 0x00}; static const u8 gemsafe_pteid_aid[] = {0x60, 0x46, 0x32, 0xFF, 0x00, 0x00, 0x02}; static const u8 gemsafe_seeid_aid[] = {0xA0, 0x00, 0x00, 0x00, 0x18, 0x0C, 0x00, 0x00, 0x01, 0x63, 0x42, 0x00}; /* static const u8 gemsafe_def_aid[] = {0xA0, 0x00, 0x00, 0x00, 0x63, 0x50, 0x4B, 0x43, 0x53, 0x2D, 0x31, 0x35}; */ typedef struct gemsafe_exdata_st { u8 aid[16]; size_t aid_len; } gemsafe_exdata; static int get_conf_aid(sc_card_t *card, u8 *aid, size_t *len) { sc_context_t *ctx = card->ctx; scconf_block *conf_block, **blocks; int i; const char *str_aid; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); conf_block = NULL; for (i = 0; ctx->conf_blocks[i] != NULL; i++) { blocks = scconf_find_blocks(ctx->conf, ctx->conf_blocks[i], "card", "gemsafeV1"); if (blocks != NULL && blocks[0] != NULL) conf_block = blocks[0]; free(blocks); } if (!conf_block) { 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_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[MAX_RESP_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); LOG_TEST_RET(ctx, 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; i = _sc_match_atr(card, gemsafe_atrs, &card->type); if (i < 0) return 0; return 1; } static int gemsafe_init(struct sc_card *card) { int r; gemsafe_exdata *exdata = NULL; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); card->name = "GemSAFE V1"; card->cla = 0x00; exdata = (gemsafe_exdata *)calloc(1, sizeof(gemsafe_exdata)); if (!exdata) return SC_ERROR_OUT_OF_MEMORY; exdata->aid_len = sizeof(exdata->aid); if(card->type == SC_CARD_TYPE_GEMSAFEV1_GENERIC) { /* try to get a AID from the config file */ r = get_conf_aid(card, exdata->aid, &exdata->aid_len); if (r < 0) { /* failed, use default value */ memcpy(exdata->aid, gemsafe_def_aid, sizeof(gemsafe_def_aid)); exdata->aid_len = sizeof(gemsafe_def_aid); } } else if (card->type == SC_CARD_TYPE_GEMSAFEV1_PTEID) { 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); } /* 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); if (r < 0) { free(exdata); sc_log(card->ctx, "applet selection failed\n"); return SC_ERROR_INVALID_CARD; } card->lock_count--; /* set the supported algorithm */ 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); } card->caps |= SC_CARD_CAP_ISO7816_PIN_INFO; card->drv_data = exdata; return SC_SUCCESS; } static int gemsafe_finish(sc_card_t *card) { gemsafe_exdata *exdata = (gemsafe_exdata *)card->drv_data; if (exdata) free(exdata); return SC_SUCCESS; } static int gemsafe_select_file(struct sc_card *card, const struct sc_path *path, struct sc_file **file_out) { /* so far just call the iso select file (but this will change) */ SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); return iso_ops->select_file(card, path, file_out); } static int gemsafe_sc2acl(sc_file_t *file, unsigned ops, u8 sc_byte) { int r; unsigned int meth = 0; if (sc_byte == 0xff) { r = sc_file_add_acl_entry(file, ops, SC_AC_NEVER, 0); return r; } if (sc_byte == 0x00) { r = sc_file_add_acl_entry(file, ops, SC_AC_NONE, 0); return r; } /* XXX: OR combination of access rights are currently not supported * hence ignored */ if (sc_byte & 0x40) meth |= SC_AC_PRO; if (sc_byte & 0x20) meth |= SC_AC_AUT | SC_AC_TERM; if (sc_byte & 0x10) meth |= SC_AC_CHV; return sc_file_add_acl_entry(file, ops, meth, sc_byte & 0x0f); } static int gemsafe_setacl(sc_card_t *card, sc_file_t *file, const u8 *data, int is_df) { int r; u8 cond; const u8 *p = data + 1; struct sc_context *ctx = card->ctx; if (is_df) { if (*data & 0x04) /* CREATE DF */ cond = *p++; else cond = 0xff; sc_log(ctx, "DF security byte CREATE DF: %02x\n", cond); r = gemsafe_sc2acl(file, SC_AC_OP_CREATE, cond); if (r < 0) return r; if (*data & 0x02) /* CREATE EF */ cond = *p; else cond = 0xff; 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 */ r = gemsafe_sc2acl(file, SC_AC_OP_CREATE, cond); if (r < 0) return r; } else { /* XXX: ACTIVATE FILE and DEACTIVATE FILE ac are currently not * supported => ignore them */ if (*data & 0x02) /* UPDATE BINARY, ERASE BINARY */ cond = *p++; else cond = 0xff; sc_log(ctx, "EF security byte UPDATE/ERASE BINARY: %02x\n", cond); r = gemsafe_sc2acl(file, SC_AC_OP_UPDATE, cond); if (r < 0) return r; r = gemsafe_sc2acl(file, SC_AC_OP_WRITE, cond); if (r < 0) return r; r = gemsafe_sc2acl(file, SC_AC_OP_ERASE, cond); if (r < 0) return r; if (*data & 0x01) /* READ BINARY */ cond = *p; else cond = 0xff; sc_log(ctx, "EF security byte READ BINARY: %02x\n", cond); r = gemsafe_sc2acl(file, SC_AC_OP_READ, cond); if (r < 0) return r; } return SC_SUCCESS; } static int gemsafe_process_fci(struct sc_card *card, struct sc_file *file, const u8 *buf, size_t len) { int r; size_t tlen; const u8 *tag = NULL, *p = buf; const char *type; struct sc_context *ctx = card->ctx; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); r = iso_ops->process_fci(card, file, buf, len); if (r < 0) return r; sc_log(ctx, "processing GemSAFE V1 specific FCI information\n"); tag = sc_asn1_find_tag(ctx, p, len, 0x82, &tlen); if (!tag) { /* no FDB => we have a DF */ type = "DF"; file->type = SC_FILE_TYPE_DF; } else { type = "EF"; file->type = SC_FILE_TYPE_WORKING_EF; } sc_log(ctx, "file type: %s\n", type); tag = sc_asn1_find_tag(ctx, p, len, 0x8C, &tlen); if (tag) { r = gemsafe_setacl(card, file, tag, strcmp(type, "DF") ? 0 : 1); if (r < 0) { sc_log(ctx, "unable to set ACL\n"); return SC_ERROR_INTERNAL; } } else sc_log(ctx, "error: AM and SC bytes missing\n"); return SC_SUCCESS; } static u8 gemsafe_flags2algref(struct sc_card *card, const struct sc_security_env *env) { u8 ret = 0; 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_TYPE_01) ret = (card->type == SC_CARD_TYPE_GEMSAFEV1_PTEID || card->type == SC_CARD_TYPE_GEMSAFEV1_SEEID) ? GEMSAFEV3_ALG_REF_FREEFORM : GEMSAFEV1_ALG_REF_FREEFORM; 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_TYPE_02) ret = (card->type == SC_CARD_TYPE_GEMSAFEV1_PTEID || card->type == SC_CARD_TYPE_GEMSAFEV1_SEEID) ? GEMSAFEV3_ALG_REF_FREEFORM : GEMSAFEV1_ALG_REF_FREEFORM; } return ret; } static int gemsafe_restore_security_env(struct sc_card *card, int se_num) { int r; struct sc_apdu apdu; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x22, 0x73, (u8) se_num); r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); } static int gemsafe_set_security_env(struct sc_card *card, const struct sc_security_env *env, int se_num) { u8 alg_ref; struct sc_security_env se_env = *env; struct sc_context *ctx = card->ctx; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); if (!(se_env.flags & SC_SEC_ENV_ALG_REF_PRESENT)) { /* set the algorithm reference */ alg_ref = gemsafe_flags2algref(card, &se_env); if (alg_ref) { se_env.algorithm_ref = alg_ref; se_env.flags |= SC_SEC_ENV_ALG_REF_PRESENT; } } if (!(se_env.flags & SC_SEC_ENV_ALG_REF_PRESENT)) 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); } static int gemsafe_compute_signature(struct sc_card *card, const u8 * data, size_t data_len, u8 * out, size_t outlen) { int r; size_t len; struct sc_apdu apdu; 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_log(ctx, "error: input data too long: %"SC_FORMAT_LEN_SIZE_T"u bytes\n", data_len); return SC_ERROR_INVALID_ARGUMENTS; } /* the Portuguese eID card requires a two-phase exchange */ /* and so does the Swedish one */ if(card->type == SC_CARD_TYPE_GEMSAFEV1_PTEID || card->type == SC_CARD_TYPE_GEMSAFEV1_SEEID) { sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x2A, 0x90, 0xA0); } else { sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x9E, 0xAC); apdu.cla |= 0x80; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = 256; } /* we sign a digestInfo object => tag 0x90 */ sbuf[0] = 0x90; sbuf[1] = (u8)data_len; memcpy(sbuf + 2, data, data_len); apdu.data = sbuf; apdu.lc = data_len + 2; apdu.datalen = data_len + 2; r = sc_transmit_apdu(card, &apdu); 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) { /* finalize the exchange */ sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x2A, 0x9E, 0x9A); apdu.le = 128; /* 1024 bit keys */ apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); if(card->type == SC_CARD_TYPE_GEMSAFEV1_SEEID) { /* cla 0x80 not supported */ apdu.cla = 0x00; } r = sc_transmit_apdu(card, &apdu); 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, (int)len); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); } static int gemsafe_decipher(struct sc_card *card, const u8 * crgram, size_t crgram_len, u8 *out, size_t outlen) { int r; struct sc_apdu apdu; u8 rbuf[MAX_RESP_BUFFER_SIZE]; sc_context_t *ctx = card->ctx; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); if (crgram_len > 255) 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, 0x84); apdu.cla |= 0x80; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = crgram_len; apdu.data = crgram; apdu.lc = crgram_len; apdu.datalen = crgram_len; 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); 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)); } static int gemsafe_get_challenge(sc_card_t *card, u8 *rnd, size_t len) { int prev_cla, r; prev_cla = card->cla; if(card->type == SC_CARD_TYPE_GEMSAFEV1_PTEID) { /* Warning: this depends on iso7816_get_challenge not * changing the value of the card's CLA */ card->cla = 0x80; } r = iso_ops->get_challenge(card, rnd, len); /* Restore the CLA value if needed */ if(card->cla != prev_cla) card->cla = prev_cla; 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 = gp_select_applet(card, exdata->aid, exdata->aid_len); } LOG_FUNC_RETURN(card->ctx, r); } static int gemsafe_logout(sc_card_t *card) { gemsafe_exdata *exdata = (gemsafe_exdata *)card->drv_data; return gp_select_applet(card, exdata->aid, exdata->aid_len); } static struct sc_card_driver *sc_get_driver(void) { struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); if (!iso_ops) iso_ops = iso_drv->ops; /* use the standard iso operations as default */ gemsafe_ops = *iso_drv->ops; /* gemsafe specific functions */ gemsafe_ops.match_card = gemsafe_match_card; gemsafe_ops.init = gemsafe_init; gemsafe_ops.finish = gemsafe_finish; gemsafe_ops.select_file = gemsafe_select_file; gemsafe_ops.restore_security_env = gemsafe_restore_security_env; gemsafe_ops.set_security_env = gemsafe_set_security_env; gemsafe_ops.decipher = gemsafe_decipher; gemsafe_ops.compute_signature = gemsafe_compute_signature; 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; } struct sc_card_driver *sc_get_gemsafeV1_driver(void) { return sc_get_driver(); } OpenSC-0.26.1/src/libopensc/card-gids.c000066400000000000000000002360121474147347300175170ustar00rootroot00000000000000/* * card-gids.c: Support for GIDS smart cards. * * Copyright (C) 2015 Vincent Le Toux (My Smart Logon) * * 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 */ /* The GIDS specification can be viewed here: https://msdn.microsoft.com/en-us/library/windows/hardware/dn642100%28v=vs.85%29.aspx and its formatting into the MS minidriver specification. Some features are undocumented like the format used to store certificates. They have been reverse engineered. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "../common/compat_strlcpy.h" #ifdef ENABLE_OPENSSL /* openssl only needed for card administration */ #include #include #endif /* ENABLE_OPENSSL */ #include "internal.h" #include "asn1.h" #include "cardctl.h" #include "iso7816.h" #ifdef ENABLE_ZLIB #include "compression.h" // used for changing the default label if used twice #include "../pkcs15init/pkcs15-init.h" #include "card-gids.h" #define GIDS_STATE_NONE 0 #define GIDS_STATE_READ_DATA_PRESENT 1 #define INS_ACTIVATE_FILE 0x44 #define INS_CREATE_FILE 0xE0 #define INS_DELETE_FILE 0xE4 #define INS_GENERAL_AUTHENTICATE 0x87 #define INS_GENERATE_ASYMECTRIC_KEY_PAIR 0x47 #define INS_GET_DATA 0xCB #define INS_MANAGE_SECURITY_ENVIRONMENT 0x22 #define INS_PUT_DATA 0xDB #define INS_SELECT 0xA4 #define INS_VERIFY 0x20 #define P1_SELECT_DF_OR_EF_WITH_EFID 0x00 #define P1_SELECT_DF_BY_NAME 0x04 #define P1_DECIPHERMENT_INTERNAL_AUTHENTICATE_KEY_AGREEMENT 0x41 #define P2_SELECT_FIRST_OR_ONLY_OCCURENCE 0x00 #define P2_PIN_DEAUTHENTICATE 0x82 #define P2_DIGITAL_SIGNATURE 0xB6 #define P2_DECIPHERMENT 0xB8 #define GIDS_PIN_STATUS_OBJECT_IDENTIFIER 0x7F71 #define GIDS_PUK_STATUS_OBJECT_IDENTIFIER 0x7F73 #define GIDS_APPLET_EFID 0x3FFF #define GIDS_PUT_KEY_DO 0x70 #define GIDS_RSA_1024_IDENTIFIER 0x06 #define GIDS_RSA_2048_IDENTIFIER 0x07 #define GIDS_RSA_3072_IDENTIFIER 0x08 #define GIDS_RSA_4096_IDENTIFIER 0x09 #define GIDS_ECC_192_IDENTIFIER 0x0A #define GIDS_ECC_224_IDENTIFIER 0x0B #define GIDS_ECC_256_IDENTIFIER 0x0C #define GIDS_ECC_384_IDENTIFIER 0x0D #define GIDS_ECC_521_IDENTIFIER 0x0E #define GIDS_PUBKEY_TAG 0x7F49 #define GIDS_PUBKEY_TAG_MODULUS 0x81 #define GIDS_PUBKEY_TAG_EXPONENT 0x82 #define GIDS_FIRST_KEY_IDENTIFIER 0x81 #define GIDS_PIN_IDENTIFIER 0x80 #define GIDS_PUK_IDENTIFIER 0x81 #define GIDS_TRY_COUNTER_OLD_TAG 0x9F17 #define GIDS_TRY_COUNTER_TAG 0x97 #define GIDS_TRY_LIMIT_TAG 0x93 #define GIDS_APPLICATION_TEMPLATE_TAG 0x61 #define GIDS_APPLICATION_AID_TAG 0x4F #define GIDS_KEY_TYPE_AT_KEYEXCHANGE 0x9A #define GIDS_KEY_TYPE_AT_SIGNATURE 0x9C static struct sc_card_operations *iso_ops; static struct sc_card_operations gids_ops; static struct sc_card_driver gids_drv = { "GIDS Smart Card", "gids", &gids_ops, NULL, 0, NULL }; struct gids_aid { int enumtag; size_t len_short; /* min length without version */ size_t len_long; /* With version and other stuff */ u8 *value; }; /* GIDS AID */ struct sc_aid gids_aid = { { 0xA0,0x00,0x00,0x03,0x97,0x42,0x54,0x46,0x59 }, 9 }; static struct gids_aid gids_aids[] = { {SC_CARD_TYPE_GIDS_V1, 9, 10, (u8 *) "\xA0\x00\x00\x03\x97\x42\x54\x46\x59\x01" }, {SC_CARD_TYPE_GIDS_V2, 9, 10, (u8 *) "\xA0\x00\x00\x03\x97\x42\x54\x46\x59\x02" }, {0, 9, 0, NULL } }; // 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; } CARD_CACHE_FILE_FORMAT, *PCARD_CACHE_FILE_FORMAT; struct gids_private_data { u8 masterfile[MAX_GIDS_FILE_SIZE]; size_t masterfilesize; u8 cmapfile[MAX_GIDS_FILE_SIZE]; size_t cmapfilesize; unsigned short currentEFID; unsigned short currentDO; int state; u8 buffer[SC_MAX_EXT_APDU_BUFFER_SIZE]; size_t buffersize; }; // LOW LEVEL API /////////////////////////////////////////// // find file identifier & DO identifier from the masterfile for a directory/file static int gids_get_identifiers(sc_card_t* card, u8* masterfile, size_t masterfilesize, char *directory, char *filename, int *fileIdentifier, int *dataObjectIdentifier) { 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); for (i = 0; i < recordcount; i++) { if (strcmp(directory, records[i].directory) == 0 && strcmp(filename, records[i].filename) == 0) { *fileIdentifier = records[i].fileIdentifier; *dataObjectIdentifier = records[i].dataObjectIdentifier; sc_log(card->ctx, "Identifiers of %s %s is fileIdentifier=%x, dataObjectIdentifier=%x\n", directory, filename, *fileIdentifier, *dataObjectIdentifier); return 0; } } sc_log(card->ctx, "file %s %s not found\n", directory, filename); return SC_ERROR_FILE_NOT_FOUND; } // used when storing a new certificates static int gids_find_available_DO(sc_card_t *card, u8* masterfile, size_t masterfilesize, int* fileIdentifier, int *dataObjectIdentifier) { // find the first available DO from the masterfile since A010 DF21 // A010 = read everyone, card user write gids_mf_record_t *records = (gids_mf_record_t *) (masterfile+1); size_t recordcount = (masterfilesize / sizeof(gids_mf_record_t)); size_t i; assert(masterfilesize >= 1); *fileIdentifier = CERT_FI; for (*dataObjectIdentifier = CARDAPPS_DO; *dataObjectIdentifier < GIDS_MAX_DO; (*dataObjectIdentifier)++) { for (i = 0; i < recordcount; i++) { if (records[i].fileIdentifier == *fileIdentifier && records[i].dataObjectIdentifier == *dataObjectIdentifier) { break; } } if (i == recordcount) { return SC_SUCCESS; } } return SC_ERROR_NOT_ENOUGH_MEMORY; } // read a DO from the card static int gids_get_DO(sc_card_t* card, int fileIdentifier, int dataObjectIdentifier, u8* response, size_t *responselen) { sc_apdu_t apdu; int r; u8 data[4] = {0x5C, 0x02, (dataObjectIdentifier&0xFF00)>>8, (dataObjectIdentifier&0xFF)}; 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_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); sc_format_apdu(card, &apdu, response == NULL ? SC_APDU_CASE_3_SHORT : SC_APDU_CASE_4_SHORT, INS_GET_DATA, (fileIdentifier&0xFF00)>>8, (fileIdentifier&0xFF)); apdu.lc = 04; apdu.data = data; apdu.datalen = 04; apdu.resp = buffer; apdu.resplen = buffer_len; apdu.le = 256; r = sc_transmit_apdu(card, &apdu); 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, buffer_len, dataObjectIdentifier, &datasize); if (!p) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_FILE_NOT_FOUND); } if (response && responselen) { if (datasize > *responselen) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_BUFFER_TOO_SMALL); } memcpy(response, p, datasize); *responselen = datasize; } return SC_SUCCESS; } // write a DO to the card static int gids_put_DO(sc_card_t* card, int fileIdentifier, int dataObjectIdentifier, u8 *data, size_t datalen) { sc_apdu_t apdu; int r; u8 buffer[SC_MAX_EXT_APDU_BUFFER_SIZE]; u8* p = buffer; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_log(card->ctx, "Got args: fileIdentifier=%x, dataObjectIdentifier=%x, data=%p, datalen=%"SC_FORMAT_LEN_SIZE_T"u\n", fileIdentifier, dataObjectIdentifier, data, datalen); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, INS_PUT_DATA, (fileIdentifier&0xFF00)>>8, (fileIdentifier&0xFF)); r = sc_asn1_put_tag(dataObjectIdentifier, data, datalen, buffer, sizeof(buffer), &p); LOG_TEST_RET(card->ctx, r, "Error handling TLV."); apdu.data = buffer; apdu.datalen = (size_t) (p - buffer); apdu.lc = apdu.datalen; apdu.flags |= SC_APDU_FLAGS_CHAINING; r = sc_transmit_apdu(card, &apdu); 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; } // select the GIDS applet static int gids_select_aid(sc_card_t* card, u8* aid, size_t aidlen, u8* response, size_t *responselen) { sc_apdu_t apdu; int r; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); 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); sc_format_apdu(card, &apdu, response == NULL ? SC_APDU_CASE_3_SHORT : SC_APDU_CASE_4_SHORT, INS_SELECT, P1_SELECT_DF_BY_NAME, P2_SELECT_FIRST_OR_ONLY_OCCURENCE); 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, "gids select failed"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); } // DIRECT FILE MANIPULATION /////////////////////////////////////////// // read a file given the masterfile static int gids_read_gidsfile_without_cache(sc_card_t* card, u8* masterfile, size_t masterfilesize, char *directory, char *filename, u8* response, size_t *responselen) { int r; int fileIdentifier; int dataObjectIdentifier; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); r = gids_get_identifiers(card, masterfile, masterfilesize, directory, filename, &fileIdentifier, &dataObjectIdentifier); LOG_TEST_RET(card->ctx, r, "unable to get the identifier for the gids file"); r = gids_get_DO(card, fileIdentifier, dataObjectIdentifier, response, responselen); LOG_TEST_RET(card->ctx, r, "unable to get the data from the file"); return r; } // write a file given the masterfile static int gids_write_gidsfile_without_cache(sc_card_t* card, u8* masterfile, size_t masterfilesize, char *directory, char *filename, u8* data, size_t datalen) { int r; int fileIdentifier; int dataObjectIdentifier; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (datalen > MAX_GIDS_FILE_SIZE) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_DATA); } r = gids_get_identifiers(card, masterfile, masterfilesize, directory, filename, &fileIdentifier, &dataObjectIdentifier); LOG_TEST_RET(card->ctx, r, "unable to get the identifier for the gids file"); r = gids_put_DO(card, fileIdentifier, dataObjectIdentifier, data, datalen); LOG_TEST_RET(card->ctx, r, "unable to get the data from the file"); return r; } // read the masterfile from the card static int gids_read_masterfile(sc_card_t* card) { struct gids_private_data* data = (struct gids_private_data*) card->drv_data; int r = SC_SUCCESS; data->masterfilesize = sizeof(data->masterfile); r = gids_get_DO(card, MF_FI, MF_DO, data->masterfile, &data->masterfilesize); if (r<0) { data->masterfilesize = sizeof(data->masterfile); LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_CARD); } if (data->masterfilesize < 1 || data->masterfile[0] != 1) { data->masterfilesize = sizeof(data->masterfile); LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_CARD); } return r; } // signal to the windows minidriver that something changed on the card and that it should refresh its cache // the format of this file is specified in the minidriver specification static int gids_update_cardcf(sc_card_t* card, int file, int container) { struct gids_private_data* data = (struct gids_private_data*) card->drv_data; u8 cardcf[6]; int r; size_t cardcfsize = sizeof(cardcf); r = gids_read_gidsfile_without_cache(card, data->masterfile, data->masterfilesize, "", "cardcf", cardcf, &cardcfsize); LOG_TEST_RET(card->ctx, r, "unable to get the cardcf"); if (file) { short filefreshness = cardcf[4] + cardcf[5] * 0x100; filefreshness++; cardcf[4] = filefreshness & 0xFF; cardcf[5] = (filefreshness>>8) & 0xFF; } if (container) { short containerfreshness = cardcf[2] + cardcf[3] * 0x100; containerfreshness++; cardcf[2] = containerfreshness & 0xFF; cardcf[3] = (containerfreshness>>8) & 0xFF; } r = gids_write_gidsfile_without_cache(card, data->masterfile, data->masterfilesize, "", "cardcf", cardcf, 6); LOG_TEST_RET(card->ctx, r, "unable to update the cardcf file"); return r; } // read a file static int gids_read_gidsfile(sc_card_t* card, char *directory, char *filename, u8* response, size_t *responselen) { struct gids_private_data* privatedata = (struct gids_private_data*) card->drv_data; int r; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (privatedata->masterfilesize == sizeof(privatedata->masterfile)) { r = gids_read_masterfile(card); 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); LOG_TEST_RET(card->ctx, r, "unable to read the file"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,r); } // 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; return gids_get_identifiers(card, privatedata->masterfile, privatedata->masterfilesize, directory, filename, &fileIdentifier, &dataObjectIdentifier); } // write a file already existing static int gids_write_gidsfile(sc_card_t* card, char *directory, char *filename, u8* data, size_t datalen) { struct gids_private_data* privatedata = (struct gids_private_data*) card->drv_data; int r; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); r = gids_update_cardcf(card, 1, 0); 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); 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; memcpy(privatedata->cmapfile, data, datalen); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,r); } // read the cmapfile (container description) static int gids_read_cmapfile(sc_card_t* card) { struct gids_private_data* data = (struct gids_private_data*) card->drv_data; int r = SC_SUCCESS; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); data->cmapfilesize = sizeof(data->cmapfile); r = gids_read_gidsfile(card, "mscp", "cmapfile", data->cmapfile, &data->cmapfilesize); if (r<0) { data->cmapfilesize = sizeof(data->cmapfile); } LOG_TEST_RET(card->ctx, r, "unable to get the cmapfile"); return r; } // create a file record in the masterfile static int gids_create_file(sc_card_t *card, char* directory, char* filename) { int r; u8 masterfilebuffer[MAX_GIDS_FILE_SIZE]; size_t masterfilebuffersize; struct gids_private_data* privatedata = (struct gids_private_data*) card->drv_data; int fileIdentifier, dataObjectIdentifier; size_t records; size_t offset; gids_mf_record_t* record; r = gids_find_available_DO(card, privatedata->masterfile, privatedata->masterfilesize, &fileIdentifier, &dataObjectIdentifier); 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); if (masterfilebuffersize > MAX_GIDS_FILE_SIZE) { SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_ENOUGH_MEMORY); } 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); strlcpy(record->filename, filename, sizeof(record->filename)); record->fileIdentifier = fileIdentifier; record->dataObjectIdentifier = dataObjectIdentifier; r = gids_update_cardcf(card, 1, 0); LOG_TEST_RET(card->ctx, r, "unable to update the cardcf"); r = gids_put_DO(card, MF_FI, MF_DO, masterfilebuffer, masterfilebuffersize); LOG_TEST_RET(card->ctx, r, "unable to update the masterfile"); memcpy(privatedata->masterfile, masterfilebuffer, masterfilebuffersize); privatedata->masterfilesize = masterfilebuffersize; return r; } // CERTIFICATE HANDLING FUNCTIONS //////////////////////////////////////////////////// // prepare a sc_path structure given a file identifier & DO // this will be an input of the gids_read_public_key function static int gids_build_certificate_path(sc_card_t* card, unsigned char containerindex, unsigned char issignatureonly,sc_path_t* cpath) { struct gids_private_data* data = (struct gids_private_data*) card->drv_data; int r, fileIdentifier, dataObjectIdentifier; char file[9]; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (issignatureonly) { snprintf(file, 9, "ksc%02X", containerindex); } else { snprintf(file, 9, "kxc%02X", containerindex); } r = gids_get_identifiers(card, data->masterfile, data->masterfilesize, "mscp", file, &fileIdentifier, &dataObjectIdentifier); if (r < 0) return SC_ERROR_OBJECT_NOT_FOUND; memset(cpath, 0, sizeof(sc_path_t)); cpath->type = SC_PATH_TYPE_PATH; cpath->len = 4; cpath->value[0] = (u8) ((fileIdentifier >> 8) & 0xFF); cpath->value[1] = (u8) fileIdentifier & 0xFF; cpath->value[2] = (u8) ((dataObjectIdentifier >> 8) & 0xFF); cpath->value[3] = (u8) dataObjectIdentifier & 0xFF; cpath->count = -1; return SC_SUCCESS; } // PIN HANDLING FUNCTIONS //////////////////////////////////////////////////// // get the pin status static int gids_get_pin_status(sc_card_t *card, int pinreference, int *tries_left, int *max_tries) { int dataObjectIdentifier, r; u8 buffer[100]; const u8* p; size_t buffersize = sizeof(buffer); size_t datasize; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (tries_left) *tries_left = -1; if (max_tries) *max_tries = -1; switch(pinreference) { case GIDS_PIN_IDENTIFIER: dataObjectIdentifier = GIDS_PIN_STATUS_OBJECT_IDENTIFIER; break; case GIDS_PUK_IDENTIFIER: dataObjectIdentifier = GIDS_PUK_STATUS_OBJECT_IDENTIFIER; break; default: LOG_FUNC_RETURN(card->ctx, SC_ERROR_OBJECT_NOT_FOUND); } r = gids_get_DO(card, GIDS_APPLET_EFID, dataObjectIdentifier, buffer, &buffersize); 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, 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, 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, buffersize , GIDS_TRY_LIMIT_TAG, &datasize); if (p && datasize == 1) { if (max_tries) *max_tries = p[0]; } 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)); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } static int gids_match_card(sc_card_t * card) { u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; int r,i; size_t resplen = sizeof(rbuf); const u8 *tag; size_t taglen; const u8 *aid; size_t aidlen; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); /* Detect by selecting applet */ r = gids_select_aid(card, gids_aid.value, gids_aid.len, rbuf, &resplen); if (r<0) return 0; card->type = SC_CARD_TYPE_GIDS_GENERIC; if (resplen > 2) { tag = sc_asn1_find_tag(card->ctx, rbuf, resplen, GIDS_APPLICATION_TEMPLATE_TAG, &taglen); if (tag != NULL) { aid = sc_asn1_find_tag(card->ctx, tag, taglen, GIDS_APPLICATION_AID_TAG, &aidlen); if (aid != NULL ) { 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) { card->type = gids_aids[i].enumtag; break; } } } } } return 1; } // extract the serial number from the cardid file static int gids_get_serialnr(sc_card_t * card, sc_serial_number_t * serial) { int r; u8 buffer[SC_MAX_EXT_APDU_BUFFER_SIZE]; size_t buffersize; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); buffersize = sizeof(buffer); r = gids_read_gidsfile(card, "", "cardid", buffer, &buffersize); LOG_TEST_RET(card->ctx, r, "unable to read cardid"); if (SC_MAX_SERIALNR < buffersize) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); } /* cache serial number */ card->serialnr.len = buffersize; memcpy(card->serialnr.value, buffer, card->serialnr.len); /* return cached serial number */ if (serial) memcpy(serial, &card->serialnr, sizeof(*serial)); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } // initialize the driver static int gids_init(sc_card_t * card) { unsigned long flags; struct gids_private_data *data; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); // cache some data in memory data = (struct gids_private_data*) calloc(1, sizeof(struct gids_private_data)); if (!data) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); } memset(data, 0, sizeof(struct gids_private_data)); card->drv_data = data; // invalidate the master file and cmap file cache data->cmapfilesize = sizeof(data->cmapfile); 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; /* 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; } // cleanup static int gids_finish(sc_card_t *card) { SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); /* free the private data */ if (card->drv_data) { free(card->drv_data); card->drv_data = NULL; } return 0; } //see 12.5.3.1 Cryptographic Mechanism Identifier for Key with CRT // the cmap file is used to detect the key algorithm / size static int gids_get_crypto_identifier_from_key_ref(sc_card_t *card, const unsigned char keyref, unsigned char *cryptoidentifier) { struct gids_private_data *data = (struct gids_private_data *) card->drv_data; PCONTAINER_MAP_RECORD records = (PCONTAINER_MAP_RECORD) data->cmapfile; int recordsnum = (int) (data->cmapfilesize / sizeof(CONTAINER_MAP_RECORD)); int index = keyref - GIDS_FIRST_KEY_IDENTIFIER; if (index >= recordsnum) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } *cryptoidentifier = 0x00; /* initialize to zero */ if (records[index].wKeyExchangeKeySizeBits == 1024 || records[index].wSigKeySizeBits == 1024) { *cryptoidentifier = GIDS_RSA_1024_IDENTIFIER; return SC_SUCCESS; } if (records[index].wKeyExchangeKeySizeBits == 2048 || records[index].wSigKeySizeBits == 2048) { *cryptoidentifier = GIDS_RSA_2048_IDENTIFIER; return SC_SUCCESS; } if (records[index].wKeyExchangeKeySizeBits == 3072 || records[index].wSigKeySizeBits == 3072) { *cryptoidentifier = GIDS_RSA_3072_IDENTIFIER; return SC_SUCCESS; } if (records[index].wKeyExchangeKeySizeBits == 4096 || records[index].wSigKeySizeBits == 4096) { *cryptoidentifier = GIDS_RSA_4096_IDENTIFIER; return SC_SUCCESS; } if (records[index].wKeyExchangeKeySizeBits == 192 || records[index].wSigKeySizeBits == 192) { *cryptoidentifier = GIDS_ECC_192_IDENTIFIER; return SC_SUCCESS; } if (records[index].wKeyExchangeKeySizeBits == 224 || records[index].wSigKeySizeBits == 224) { *cryptoidentifier = GIDS_ECC_224_IDENTIFIER; return SC_SUCCESS; } if (records[index].wKeyExchangeKeySizeBits == 256 || records[index].wSigKeySizeBits == 256) { *cryptoidentifier = GIDS_ECC_256_IDENTIFIER; return SC_SUCCESS; } if (records[index].wKeyExchangeKeySizeBits == 384 || records[index].wSigKeySizeBits == 384) { *cryptoidentifier = GIDS_ECC_384_IDENTIFIER; return SC_SUCCESS; } if (records[index].wKeyExchangeKeySizeBits == 521 || records[index].wSigKeySizeBits == 521) { *cryptoidentifier = GIDS_ECC_521_IDENTIFIER; return SC_SUCCESS; } LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } // same here static u8 gids_get_crypto_identifier_from_prkey_info(struct sc_pkcs15_prkey_info *key_info) { if (key_info->modulus_length > 0) { if (key_info->modulus_length == 1024) { return GIDS_RSA_1024_IDENTIFIER; } if (key_info->modulus_length == 2048) { return GIDS_RSA_2048_IDENTIFIER; } if (key_info->modulus_length == 3072) { return GIDS_RSA_3072_IDENTIFIER; } if (key_info->modulus_length == 4096) { return GIDS_RSA_4096_IDENTIFIER; } return 0; } else { return 0; } } // GIDS implementation of set security environment static int gids_set_security_env(sc_card_t *card, const sc_security_env_t *env, int se_num) { struct sc_apdu apdu; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; u8 *p; int r, locked = 0; assert(card != NULL && env != NULL); 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) { case SC_SEC_OPERATION_DECIPHER: apdu.p2 = P2_DECIPHERMENT; break; case SC_SEC_OPERATION_SIGN: apdu.p2 = P2_DIGITAL_SIGNATURE; break; default: return SC_ERROR_INVALID_ARGUMENTS; } p = sbuf; if (env->flags & SC_SEC_ENV_ALG_REF_PRESENT) { return SC_ERROR_NOT_SUPPORTED; } else { // ALG REF is mandatory *p++ = 0x80; /* algorithm reference */ *p++ = 0x01; gids_get_crypto_identifier_from_key_ref(card,env->key_ref[0],p); if (env->operation == SC_SEC_OPERATION_DECIPHER) { *p++ |= 0x40; } else { *p++ |= 0x50; } } if (!(env->flags & SC_SEC_ENV_KEY_REF_PRESENT)) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } 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); memcpy(p, env->key_ref, env->key_ref_len); p += env->key_ref_len; r = (int) (p - sbuf); apdu.lc = r; apdu.datalen = r; 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 != 0) { 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) return 0; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, INS_MANAGE_SECURITY_ENVIRONMENT, 0xF2, se_num); r = sc_transmit_apdu(card, &apdu); sc_unlock(card); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); err: if (locked) sc_unlock(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); 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)); } // read a public key static int gids_read_public_key (struct sc_card *card , unsigned int algorithm, struct sc_path * path, unsigned key_reference, unsigned modulus_length, unsigned char **response, size_t *responselen) { struct sc_pkcs15_pubkey_rsa rsa_key; sc_apdu_t apdu; size_t tlen, len; const u8* keytemplate; const u8* keydata; int r; u8 data[] = {0x70, 0x08, // retrieve key 0x84, 0x01, key_reference, // key reference 0xA5, 0x03, 0x7F, 0x49, 0x80 // key value template: only public key }; u8 buffer[SC_MAX_EXT_APDU_BUFFER_SIZE]; size_t buffersize = sizeof(buffer); SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); 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); sc_format_apdu(card, &apdu, response == NULL ? SC_APDU_CASE_3_SHORT : SC_APDU_CASE_4_SHORT, INS_GET_DATA, 0x3F, 0xFF); apdu.lc = sizeof(data); apdu.data = data; apdu.datalen = sizeof(data); apdu.resp = buffer; apdu.resplen = buffersize; apdu.le = 256; r = sc_transmit_apdu(card, &apdu); 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_log(card->ctx, "invalid public key data: missing tag"); LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); } keydata = sc_asn1_find_tag(card->ctx, keytemplate, tlen, GIDS_PUBKEY_TAG_MODULUS, &len); if (keydata != NULL) { rsa_key.modulus.data = (u8*) keydata; rsa_key.modulus.len = len; } else { rsa_key.modulus.len = 0; } keydata = sc_asn1_find_tag(card->ctx, keytemplate, tlen, GIDS_PUBKEY_TAG_EXPONENT, &len); if (keydata != NULL) { rsa_key.exponent.data = (u8*) keydata; rsa_key.exponent.len = len; } else { rsa_key.exponent.len = 0; } if (rsa_key.exponent.len && rsa_key.modulus.len) { 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_log(card->ctx, "it is not a known public key"); LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); } if (response && responselen) sc_log_hex(card->ctx, "encoded public key", *response, *responselen); return SC_SUCCESS; } // emulate a filesystem given EF and DO static int gids_select_file(sc_card_t *card, const struct sc_path *in_path, struct sc_file **file_out) { struct sc_file *file = NULL; struct sc_context *ctx = card->ctx; struct gids_private_data *data = (struct gids_private_data *) card->drv_data; LOG_FUNC_CALLED(card->ctx); data->state = GIDS_STATE_NONE; data->currentDO = 0; data->currentEFID = 0; if (in_path->len == 4 && in_path->value[0] == 0xA0) { // is it a DO pseudo file ? // yes, succeed data->currentEFID = in_path->value[1] + (in_path->value[0]<<8); data->currentDO = in_path->value[3] + (in_path->value[2]<<8); 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 // replace the file selection from SC_PATH_TYPE_PATH to SC_PATH_TYPE_FILE_ID struct sc_path key_path; memset(&key_path, 0, sizeof(key_path)); key_path.len = 2; key_path.value[0] = in_path->value[2]; key_path.value[1] = in_path->value[3]; key_path.type = SC_PATH_TYPE_FILE_ID; return iso_ops->select_file(card, &key_path, file_out); } else { return iso_ops->select_file(card, in_path, file_out); } } 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) { 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.encoding = SC_PIN_ENCODING_ASCII; data->pin1.offset = 5; data->pin1.logged_in = SC_PIN_STATE_UNKNOWN; return SC_SUCCESS; } static int gids_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_left) { 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); } } // 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) { struct gids_private_data *data = (struct gids_private_data *) card->drv_data; struct sc_context *ctx = card->ctx; int r; int size; LOG_FUNC_CALLED(card->ctx); if (! data->currentDO || ! data->currentEFID) { LOG_FUNC_RETURN(ctx, SC_ERROR_INTERNAL); } if (data->state != GIDS_STATE_READ_DATA_PRESENT) { // this function is called to read the certificate only u8 buffer[SC_MAX_EXT_APDU_BUFFER_SIZE]; size_t buffersize = sizeof(buffer); r = gids_get_DO(card, data->currentEFID, data->currentDO, buffer, &(buffersize)); if (r <0) return r; if (buffersize < 4) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_DATA); } if (buffer[0] == 1 && buffer[1] == 0) { if (flags) *flags |= SC_FILE_FLAG_COMPRESSED_ZLIB; /* compressed data are starting on position buffer + 4 */ data->buffersize = sizeof(data->buffer) - 4; memcpy(data->buffer, buffer + 4, buffersize); } else { 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; } if (offset >= data->buffersize) { return 0; } size = (int) MIN((data->buffersize - offset), count); memcpy(buf, data->buffer + offset, size); return size; } // refresh the internal caches and return the number of containers static int gids_get_all_containers(sc_card_t* card, size_t *recordsnum) { int r; struct gids_private_data *privatedata = (struct gids_private_data *) card->drv_data; r = gids_read_masterfile(card); LOG_TEST_RET(card->ctx, r, "unable to read the masterfile"); r = gids_read_cmapfile(card); LOG_TEST_RET(card->ctx, r, "unable to read the cmapfile"); *recordsnum = (privatedata ->cmapfilesize / sizeof(CONTAINER_MAP_RECORD)); return SC_SUCCESS; } // return the detail about a container to emulate a pkcs15 card static int gids_get_container_detail(sc_card_t* card, sc_cardctl_gids_get_container_t* container) { PCONTAINER_MAP_RECORD records = NULL; struct gids_private_data *privatedata = (struct gids_private_data *) card->drv_data; size_t recordsnum, num, i; records = (PCONTAINER_MAP_RECORD) privatedata ->cmapfile; recordsnum = (privatedata ->cmapfilesize / sizeof(CONTAINER_MAP_RECORD)); num = container->containernum ; if (num >= recordsnum) { return SC_ERROR_OBJECT_NOT_FOUND; } memset(container, 0, sizeof(sc_cardctl_gids_get_container_t)); container->containernum = num; if (!(records[num].bFlags & CONTAINER_MAP_VALID_CONTAINER)) { return SC_SUCCESS; } // ignore problematic containers if (records[num].wKeyExchangeKeySizeBits > 0 && records[num].wSigKeySizeBits > 0) { return SC_SUCCESS; } if (records[num].wKeyExchangeKeySizeBits == 0 && records[num].wSigKeySizeBits == 0) { return SC_SUCCESS; } for (i = 0; i < MAX_CONTAINER_NAME_LEN; i++) { container->label[i] = (char) records[num].wszGuid[i]; } container->label[MAX_CONTAINER_NAME_LEN] = 0; container->module_length = MAX(records[num].wKeyExchangeKeySizeBits, records[num].wSigKeySizeBits); container->prvusage = SC_PKCS15_PRKEY_USAGE_SIGN; container->pubusage = SC_PKCS15_PRKEY_USAGE_VERIFY; if (records[num].wKeyExchangeKeySizeBits > 0) { container->prvusage |= SC_PKCS15_PRKEY_USAGE_DECRYPT; container->pubusage |= SC_PKCS15_PRKEY_USAGE_ENCRYPT; } // do not check for return code, typically if there is no certificate associated to the key gids_build_certificate_path(card, (unsigned char) num, (records[num].wSigKeySizeBits > 0), &(container->certificatepath)); return SC_SUCCESS; } // find a new key reference static int gids_select_key_reference(sc_card_t *card, sc_pkcs15_prkey_info_t* key_info) { struct gids_private_data *data = (struct gids_private_data *) card->drv_data; PCONTAINER_MAP_RECORD records = (PCONTAINER_MAP_RECORD) data->cmapfile; size_t recordsnum; int r; char ch_tmp[10]; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); // refresh the cached data in case some thing has been modified r = gids_read_masterfile(card); LOG_TEST_RET(card->ctx, r, "gids read masterfile failed"); r = gids_read_cmapfile(card); LOG_TEST_RET(card->ctx, r, "gids read cmapfile failed"); recordsnum = (data->cmapfilesize / sizeof(CONTAINER_MAP_RECORD)); if (!key_info->key_reference) { // new key size_t i; // search for a key number not used anymore for (i = 0; i < recordsnum; i++) { if (!(records[i].bFlags & CONTAINER_MAP_VALID_CONTAINER)) { key_info->key_reference = (int) (GIDS_FIRST_KEY_IDENTIFIER + i); return SC_SUCCESS; } } // use a new key number if (recordsnum > GIDS_MAX_CONTAINER) { 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_log(card->ctx, "invalid key ref %d", key_info->key_reference); LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } if (i > recordsnum) { sc_log(card->ctx, "container num is not allowed %"SC_FORMAT_LEN_SIZE_T"u %"SC_FORMAT_LEN_SIZE_T"u", i, recordsnum); LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } } 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; } // perform the creation of the key file // try to mimic the GIDS minidriver key permission static int gids_perform_create_keyfile(sc_card_t *card, u8 keytype, u8 kid, u8 algid) { struct sc_apdu apdu; int r; u8 keyexchange[] = {0x62,0x47, 0x82,0x01,0x18, // file type 0x83,0x02,0xB0,kid, // key id = 81 0x8C,0x05,0x8F,0x10,0x10,0x10,0x00, // security 0xA5,0x37, 0xB8,0x09, // confidentiality 0x80,0x01,algid, //algo: rsa without padding 0x83,0x01,kid, // key id 0x95,0x01,0x40, // usage 0xB8,0x09, // confidentiality 0x80,0x01,0x80 + algid, // RSAES-OAEP padding 0x83,0x01,kid, 0x95,0x01,0x40, 0xB8,0x09, // confidentiality 0x80,0x01,0x40 + algid, // RSAES-PKCS1-v1_5 padding 0x83,0x01,kid, 0x95,0x01,0x40, 0xB6,0x09, // signature 0x80,0x01,0x10 + algid, // Full SHA off-card authorized 0x83,0x01,kid, 0x95,0x01,0x40, 0xB6,0x09, // signature 0x80,0x01,0x50 + algid, // RSASSA PKCS1-v 1_5 padding scheme (for RSA only; otherwise, RFU) 0x83,0x01,kid, 0x95,0x01,0x40 }; u8 sign[] = {0x62,0x26, 0x82,0x01,0x18, // file type 0x83,0x02,0xB0,kid, // key id = 81 0x8C,0x05,0x8F,0x10,0x10,0x10,0x00, // security 0xA5,0x16, 0xB6,0x09, // signature 0x80,0x01,0x10+ algid, // Full SHA off-card authorized 0x83,0x01,0x83, // key id 0x95,0x01,0x40, // usage 0xB6,0x09, // signature 0x80,0x01,0x50 + algid, // RSASSA PKCS1-v 1_5 padding scheme (for RSA only; otherwise, RFU) 0x83,0x01,0x83, 0x95,0x01,0x40}; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); // create the key file sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, INS_CREATE_FILE, 0x00, 0x00); if (keytype == 1) { apdu.lc = sizeof(keyexchange); apdu.datalen = sizeof(keyexchange); apdu.data = keyexchange; } else if (keytype == 2) { apdu.lc = sizeof(sign); apdu.datalen = sizeof(sign); apdu.data = sign; } else { LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } 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"); // activate file sc_format_apdu(card, &apdu, SC_APDU_CASE_1, INS_ACTIVATE_FILE, 0x00, 0x00); 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, "ACTIVATE_FILE returned error"); LOG_FUNC_RETURN(card->ctx, r); } // perform the creation of the keyfile and its registration in the cmapfile and keymap file static int gids_create_keyfile(sc_card_t *card, sc_pkcs15_object_t *object) { int r; u8 keytype; struct sc_pkcs15_prkey_info *key_info = (struct sc_pkcs15_prkey_info *) object->data; u8 kid = key_info->key_reference; u8 algid = gids_get_crypto_identifier_from_prkey_info(key_info); u8 cmapbuffer[MAX_GIDS_FILE_SIZE]; size_t cmapbuffersize = 0; u8 keymapbuffer[MAX_GIDS_FILE_SIZE]; size_t keymapbuffersize = 0; size_t keymaprecordnum = 0; struct gids_private_data *data = (struct gids_private_data *) card->drv_data; size_t recordnum; size_t containernum = key_info->key_reference - GIDS_FIRST_KEY_IDENTIFIER; PCONTAINER_MAP_RECORD records = ((PCONTAINER_MAP_RECORD) cmapbuffer) + containernum; struct gids_keymap_record* keymaprecord = NULL; int i; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); // sanity check assert((object->type & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_PRKEY); if (!algid) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } // masterfile & cmapfile have been refreshed in gids_perform_create_keyfile recordnum = (data->cmapfilesize / sizeof(CONTAINER_MAP_RECORD)); // sanity check if (containernum > recordnum || containernum > GIDS_MAX_CONTAINER) 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 if (recordnum > 0) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); } // else can be empty if not record keymapbuffersize = 0; } else { keymaprecordnum = (keymapbuffersize - 1) / sizeof(struct gids_keymap_record); if (keymaprecordnum != recordnum) { 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); LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); } } // prepare the cmap & keymap buffer if (containernum == recordnum) { // reserve space on the cmap file memset(cmapbuffer, 0, sizeof(cmapbuffer)); memcpy(cmapbuffer, data->cmapfile, data->cmapfilesize); cmapbuffersize = data->cmapfilesize + sizeof(CONTAINER_MAP_RECORD); r = gids_write_gidsfile(card, "mscp", "cmapfile", cmapbuffer, cmapbuffersize); LOG_TEST_RET(card->ctx, r, "unable to reserve space on the cmapfile"); if (keymapbuffersize == 0) { keymapbuffersize = 1; keymapbuffer[0] = 1; } keymapbuffersize += sizeof(struct gids_keymap_record); } else { memcpy(cmapbuffer, data->cmapfile, data->cmapfilesize); cmapbuffersize = data->cmapfilesize; } keymaprecord = ((struct gids_keymap_record*)(keymapbuffer +1)) + containernum; memset(records, 0, sizeof(CONTAINER_MAP_RECORD)); memset(keymaprecord, 0, sizeof(struct gids_keymap_record)); if (key_info->usage & SC_PKCS15_PRKEY_USAGE_DECRYPT) { keytype = 1; // AT_KEYEXCHANGE records->wKeyExchangeKeySizeBits = (unsigned short) key_info->modulus_length; keymaprecord->keytype = GIDS_KEY_TYPE_AT_KEYEXCHANGE; } else if (key_info->usage & SC_PKCS15_PRKEY_USAGE_SIGN) { keytype = 2; // AT_SIGNATURE records->wSigKeySizeBits = (unsigned short) key_info->modulus_length; keymaprecord->keytype = GIDS_KEY_TYPE_AT_SIGNATURE; } else { LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } //the GIDS card must have unique container names // avoid the problem with the default label by making it unique 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); } // convert char to wchar for(i = 0; i < MAX_CONTAINER_NAME_LEN && object->label[i]; i++) { records->wszGuid[i] = object->label[i]; } // TODO: check if a container with the same name already exists and prevent is creation or change its name records->bFlags = CONTAINER_MAP_VALID_CONTAINER; if (recordnum == 0) { records->bFlags |= CONTAINER_MAP_DEFAULT_CONTAINER; } keymaprecord->algid = algid; keymaprecord->state = 1; keymaprecord->unknownWithFFFF = (unsigned short) (-1); keymaprecord->keyref = 0xB000 + kid; r = gids_perform_create_keyfile(card, keytype, kid, algid); LOG_TEST_RET(card->ctx, r, "unable to create the key file"); r = gids_update_cardcf(card, 0, 1); 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); LOG_TEST_RET(card->ctx, r, "unable to write the keymap file"); r = gids_write_gidsfile(card, "mscp", "cmapfile", cmapbuffer, cmapbuffersize); LOG_TEST_RET(card->ctx, r, "unable to write the cmap file after the container creation"); LOG_FUNC_RETURN(card->ctx, r); } // generate a key on an existing container static int gids_generate_key(sc_card_t *card, sc_pkcs15_object_t *object, struct sc_pkcs15_pubkey* pubkey) { struct sc_pkcs15_prkey_info *key_info = (struct sc_pkcs15_prkey_info *) object->data; u8 kid = key_info->key_reference; u8 algid = gids_get_crypto_identifier_from_prkey_info(key_info); struct sc_apdu apdu; u8 generatekey[] = {0xAC, 0x06, // CRT template 0x80, 0x01, algid, // algorithm 0x83, 0x01, kid // key reference }; int r; u8 *buffer = NULL; 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 ((key_info->key_reference > GIDS_FIRST_KEY_IDENTIFIER + GIDS_MAX_CONTAINER) || (kid < GIDS_FIRST_KEY_IDENTIFIER)) { 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); apdu.lc = sizeof(generatekey); apdu.datalen = sizeof(generatekey); apdu.data = generatekey; 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, "generate key returned error"); r = gids_read_public_key(card, 0, NULL, kid, 0, &buffer, &buffersize); 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); LOG_FUNC_RETURN(card->ctx, r); } // import the key in an existing container static int gids_import_key(sc_card_t *card, sc_pkcs15_object_t *object, sc_pkcs15_prkey_t *key) { struct sc_pkcs15_prkey_info *key_info = (struct sc_pkcs15_prkey_info *) object->data; int version = 0; int keytype = 2; // RSA u8 kid = key_info->key_reference; size_t len = 1; int encryptkeyref = 0; //NONE int r; u8* buffer = NULL; size_t buflen = 0; struct sc_asn1_entry asn1_key_usage_template[] = { { "keyReference", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_OCTET_STRING | SC_ASN1_CTX, 0, NULL, NULL }, { "KeyValueTemplate", SC_ASN1_STRUCT, SC_ASN1_TAG_NULL | SC_ASN1_CTX | SC_ASN1_CONS, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; struct sc_asn1_entry asn1_key_value_template[] = { { "keyType", SC_ASN1_INTEGER, SC_ASN1_TAG_BIT_STRING | SC_ASN1_CTX, 0, NULL, NULL }, { "encryptKeyRef", SC_ASN1_INTEGER, SC_ASN1_TAG_OCTET_STRING | SC_ASN1_CTX, 0, NULL, NULL }, { "keyValue", SC_ASN1_STRUCT, SC_ASN1_TAG_OBJECT_DESCRIPTOR | SC_ASN1_CTX, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; struct sc_asn1_entry asn1_key_data[] = { { "keyData", SC_ASN1_STRUCT, SC_ASN1_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; struct sc_asn1_entry asn1_rsa_priv_coefficients_gids[] = { { "version", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, { "modulus", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_INTEGER, SC_ASN1_ALLOC, NULL, NULL }, { "publicExponent", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_INTEGER, SC_ASN1_ALLOC, NULL, NULL }, { "privateExponent", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_INTEGER, SC_ASN1_ALLOC, NULL, NULL }, { "p", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_INTEGER, SC_ASN1_ALLOC, NULL, NULL }, { "q", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_INTEGER, SC_ASN1_ALLOC, NULL, NULL }, { "dmp1", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_INTEGER, SC_ASN1_ALLOC, NULL, NULL }, { "dmq1", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_INTEGER, SC_ASN1_ALLOC, NULL, NULL }, { "iqmp", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_INTEGER, SC_ASN1_ALLOC, NULL, NULL }, { 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_PRKEY_RSA || key->algorithm != SC_ALGORITHM_RSA) { 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_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); sc_format_asn1_entry(asn1_rsa_priv_coefficients_gids + 1, key->u.rsa.modulus.data, &key->u.rsa.modulus.len, 1); sc_format_asn1_entry(asn1_rsa_priv_coefficients_gids + 2, key->u.rsa.exponent.data, &key->u.rsa.exponent.len, 1); sc_format_asn1_entry(asn1_rsa_priv_coefficients_gids + 3, key->u.rsa.d.data, &key->u.rsa.d.len, 1); sc_format_asn1_entry(asn1_rsa_priv_coefficients_gids + 4, key->u.rsa.p.data, &key->u.rsa.p.len, 1); sc_format_asn1_entry(asn1_rsa_priv_coefficients_gids + 5, key->u.rsa.q.data, &key->u.rsa.q.len, 1); sc_format_asn1_entry(asn1_rsa_priv_coefficients_gids + 6, key->u.rsa.dmp1.data, &key->u.rsa.dmp1.len, 1); sc_format_asn1_entry(asn1_rsa_priv_coefficients_gids + 7, key->u.rsa.dmq1.data, &key->u.rsa.dmq1.len, 1); sc_format_asn1_entry(asn1_rsa_priv_coefficients_gids + 8, key->u.rsa.iqmp.data, &key->u.rsa.iqmp.len, 1); sc_format_asn1_entry(asn1_key_data + 0, asn1_rsa_priv_coefficients_gids, NULL, 1); sc_format_asn1_entry(asn1_key_value_template + 0, &keytype, NULL, 1); sc_format_asn1_entry(asn1_key_value_template + 1, &encryptkeyref, NULL, 1); sc_format_asn1_entry(asn1_key_value_template + 2, asn1_key_data, NULL, 1); sc_format_asn1_entry(asn1_key_usage_template + 0, &kid, &len, 1); 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_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_VERBOSE, r, "unable to put the private key - key greater than 2048 bits ?"); r = SC_SUCCESS; err: sc_mem_clear(buffer, buflen); LOG_FUNC_RETURN(card->ctx, r); } // remove a crt file 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), "3FFFB0%02X", (u8) (0xFF & (containernum + GIDS_FIRST_KEY_IDENTIFIER))); sc_format_path(ch_tmp, &cpath); 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); LOG_TEST_RET(card->ctx, r, "unable to delete the key file"); return r; } // encode a certificate using the minidriver compression static int gids_encode_certificate(sc_card_t *card, u8* source, size_t sourcesize, u8* destination, size_t* destinationsize) { int r; size_t outlen; if (*destinationsize < 4) { return SC_ERROR_BUFFER_TOO_SMALL; } if (sourcesize > 0xFFFF) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); } // format is: // 2 bytes for compression version // 2 bytes for uncompressed file size // ZLIB compression of the certificate destination[0] = 1; destination[1] = 0; destination[2] = sourcesize & 0xFF; destination[3] = (sourcesize & 0xFF00) >> 8; outlen = *destinationsize - 4; r = sc_compress(destination + 4, &outlen, source, sourcesize, COMPRESSION_ZLIB); LOG_TEST_RET(card->ctx, r, "unable to compress the certificate"); *destinationsize = outlen + 4; return SC_SUCCESS; } // save a certificate associated to a container to the card static int gids_save_certificate(sc_card_t *card, sc_pkcs15_object_t *certobject, sc_pkcs15_object_t *privkeyobject, struct sc_path *path) { int r; u8 certbuffer[MAX_GIDS_FILE_SIZE]; size_t certbuffersize = sizeof(certbuffer); struct sc_pkcs15_cert_info *cert_info = (struct sc_pkcs15_cert_info *) certobject->data; 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); // refresh the cached data in case some thing has been modified r = gids_read_masterfile(card); LOG_TEST_RET(card->ctx, r, "gids read masterfile failed"); r= gids_read_cmapfile(card); 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); 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; if (!(prkey_info->usage & SC_PKCS15_PRKEY_USAGE_DECRYPT)) { snprintf(filename, sizeof(filename), "ksc%02X", containernum); } else { snprintf(filename, sizeof(filename), "kxc%02X", containernum); } r = gids_does_file_exists(card, "mscp", filename); if (r == SC_ERROR_FILE_NOT_FOUND) { r = gids_create_file(card, "mscp", filename); LOG_TEST_RET(card->ctx, r, "gids unable to create the certificate file"); } r = gids_write_gidsfile(card, "mscp", filename, certbuffer, certbuffersize); 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); LOG_TEST_RET(card->ctx, r, "gids unable to build the certificate path"); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } // remove a container and its registration in the cmapfile static int gids_delete_container_num(sc_card_t *card, size_t containernum) { int r; u8 cmapbuffer[MAX_GIDS_FILE_SIZE]; size_t cmapbuffersize = 0; u8 keymapbuffer[MAX_GIDS_FILE_SIZE]; size_t keymapbuffersize = 0; size_t keymaprecordnum = 0; struct gids_private_data *data = (struct gids_private_data *) card->drv_data; size_t recordnum; PCONTAINER_MAP_RECORD records = ((PCONTAINER_MAP_RECORD) cmapbuffer) + containernum; struct gids_keymap_record* keymaprecord = NULL; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); // masterfile & cmapfile have been refreshed before recordnum = (data->cmapfilesize / sizeof(CONTAINER_MAP_RECORD)); // sanity check if (containernum >= recordnum || recordnum > GIDS_MAX_CONTAINER) 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 LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); } keymaprecordnum = (keymapbuffersize - 1) / sizeof(struct gids_keymap_record); if (keymaprecordnum != recordnum) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); } // update the key map file memcpy(cmapbuffer, data->cmapfile, data->cmapfilesize); cmapbuffersize = data->cmapfilesize; keymaprecord = ((struct gids_keymap_record*)(keymapbuffer +1)) + containernum; memset(records, 0, sizeof(CONTAINER_MAP_RECORD)); memset(keymaprecord, 0, sizeof(struct gids_keymap_record)); keymaprecord->unknownWithFFFF = (unsigned short) (-1); keymaprecord->keyref =(unsigned short) (-1); // remove the key, update the key map & cmap file and signal the change r = gids_delete_key_file(card, (int) containernum); LOG_TEST_RET(card->ctx, r, "unable to delete the key file"); r = gids_update_cardcf(card, 0, 1); 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); LOG_TEST_RET(card->ctx, r, "unable to write the keymap file"); r = gids_write_gidsfile(card, "mscp", "cmapfile", cmapbuffer, cmapbuffersize); LOG_TEST_RET(card->ctx, r, "unable to write the cmap file after the container creation"); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } // delete a certificate associated to a container static int gids_delete_cert(sc_card_t *card, sc_pkcs15_object_t* object) { int r; struct gids_private_data *privatedata = (struct gids_private_data *) card->drv_data; struct sc_pkcs15_cert_info *cert_info = (struct sc_pkcs15_cert_info *) object->data; unsigned short fileIdentifier, DO; u8 masterfilebuffer[MAX_GIDS_FILE_SIZE]; size_t masterfilebuffersize = 0; 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); // refresh the cached data in case some thing has been modified r = gids_read_masterfile(card); LOG_TEST_RET(card->ctx, r, "gids read masterfile failed"); r= gids_read_cmapfile(card); LOG_TEST_RET(card->ctx, r, "gids read cmapfile failed"); // remove the file reference from the masterfile if (cert_info->path.len != 4) { 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]; memcpy(masterfilebuffer, privatedata->masterfile, privatedata->masterfilesize); masterfilebuffersize = privatedata->masterfilesize; 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; break; } } if (recordnum == (size_t) -1) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_FILE_NOT_FOUND); } 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); LOG_TEST_RET(card->ctx, r, "unable to update the cache file"); r = gids_put_DO(card, fileIdentifier, DO, NULL, 0); LOG_TEST_RET(card->ctx, r, "gids unable to delete the certificate DO"); r = gids_put_DO(card, MF_FI, MF_DO, masterfilebuffer, masterfilebuffersize); LOG_TEST_RET(card->ctx, r, "gids unable to update the masterfile"); memcpy(privatedata->masterfile, masterfilebuffer, masterfilebuffersize); privatedata->masterfilesize = masterfilebuffersize; LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } static int gids_delete_key(sc_card_t *card, sc_pkcs15_object_t* object) { int r; 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); // refresh the cached data in case some thing has been modified r = gids_read_masterfile(card); LOG_TEST_RET(card->ctx, r, "gids read masterfile failed"); r = gids_read_cmapfile(card); 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); LOG_TEST_RET(card->ctx, r, "gids unable to delete the container"); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } // used by gids_initialize to create the filesystem static int gids_initialize_create_file(sc_card_t *card, u8* command, size_t commandsize) { int r; sc_apdu_t apdu; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, INS_CREATE_FILE, 0x00, 0x00); apdu.lc = commandsize; apdu.data = command; apdu.datalen = commandsize; r = sc_transmit_apdu(card, &apdu); 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); 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 static int gids_set_administrator_key(sc_card_t *card, u8* key) { int r; u8 adminKeyData[] = {0x84,0x01,0x80, // key reference 0xA5,0x1F, // key template // key value 0x87,0x18,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x01, 0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x01,0x02, 0x03,0x04,0x05,0x06,0x07,0x08, // key file 0x88, 0x03,0xB0,0x73,0xDC}; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); 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)); LOG_TEST_RET(card->ctx, r, "gids unable to set the admin key"); return SC_SUCCESS; } static int gids_check_that_card_is_new(sc_card_t *card) { int r; // retrieve the masterfile // if it succeed, the card has already been initialized r = gids_read_masterfile(card); if (r == SC_SUCCESS) { r = SC_ERROR_INVALID_CARD; LOG_TEST_RET(card->ctx, r, "unable to read the masterfile"); } return SC_SUCCESS; } // initialize a card // see the minidriver specification annex for the details about this static int gids_initialize(sc_card_t *card, sc_cardctl_gids_init_param_t* param) { sc_apdu_t apdu; int r; #ifdef ENABLE_OPENSSL int i; #endif // hardcoded file setting // File type=39=TLV structure for BER-TLV DOs then ACL varies depending on the file) // this DO EF are used like DF file so the permission has to be set only once u8 UserCreateDeleteDirAc[] = {0x62,0x0C,0x82,0x01,0x39,0x83,0x02,0xA0,0x00,0x8C,0x03,0x03,0x30,0x00}; u8 EveryoneReadUserWriteAc[] = {0x62,0x0C,0x82,0x01,0x39,0x83,0x02,0xA0,0x10,0x8C,0x03,0x03,0x30,0x00}; u8 UserWriteExecuteAc[] = {0x62,0x0C,0x82,0x01,0x39,0x83,0x02,0xA0,0x11,0x8C,0x03,0x03,0x30,0xFF}; 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 = 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. u8 masterfile[] = {0x01,0x6d,0x73,0x63,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xa0,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x63,0x61,0x72,0x64,0x69,0x64,0x00,0x00,0x00,0x00,0x00,0x20,0xdf, 0x00,0x00,0x12,0xa0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x63,0x61, 0x72,0x64,0x61,0x70,0x70,0x73,0x00,0x00,0x00,0x21,0xdf,0x00,0x00,0x10,0xa0,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x63,0x61,0x72,0x64,0x63,0x66,0x00,0x00, 0x00,0x00,0x00,0x22,0xdf,0x00,0x00,0x10,0xa0,0x00,0x00,0x6d,0x73,0x63,0x70,0x00,0x00, 0x00,0x00,0x00,0x63,0x6d,0x61,0x70,0x66,0x69,0x6c,0x65,0x00,0x00,0x00,0x23,0xdf,0x00, 0x00,0x10,0xa0,0x00,0x00}; // list the application on the card - defined in the minidriver specification u8 cardapps[] = {0x6d,0x73,0x63,0x70,0x00,0x00,0x00,0x00}; // used to detect if modifications have been done outside of the minidriver - defined in the minidriver specification u8 cardcf[] = {0x00,0x00,0x00,0x00,0x00,0x00}; struct sc_pin_cmd_data pindata; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); // avoid multiple initialization r = gids_check_that_card_is_new(card); LOG_TEST_RET(card->ctx, r, "card seems to have been already initialized"); memset(&pindata, 0, sizeof(pindata)); // create PIN & PUK pindata.cmd = SC_PIN_CMD_CHANGE; pindata.pin_type = SC_AC_CHV; pindata.pin2.len = param->user_pin_len; pindata.pin2.data = param->user_pin; pindata.pin_reference = 0x80; r = sc_pin_cmd(card, &pindata, NULL); LOG_TEST_RET(card->ctx, r, "gids set pin"); // create file r = gids_initialize_create_file(card, UserCreateDeleteDirAc, sizeof(UserCreateDeleteDirAc)); LOG_TEST_RET(card->ctx, r, "gids to create the file UserCreateDeleteDirAc"); r = gids_initialize_create_file(card, EveryoneReadUserWriteAc, sizeof(EveryoneReadUserWriteAc)); LOG_TEST_RET(card->ctx, r, "gids to create the file EveryoneReadUserWriteAc"); r = gids_initialize_create_file(card, UserWriteExecuteAc, sizeof(UserWriteExecuteAc)); LOG_TEST_RET(card->ctx, r, "gids to create the file UserWriteExecuteAc"); r = gids_initialize_create_file(card, EveryoneReadAdminWriteAc, sizeof(EveryoneReadAdminWriteAc)); LOG_TEST_RET(card->ctx, r, "gids to create the file EveryoneReadAdminWriteAc"); r = gids_initialize_create_file(card, UserReadWriteAc, sizeof(UserReadWriteAc)); LOG_TEST_RET(card->ctx, r, "gids to create the file UserReadWriteAc"); r = gids_initialize_create_file(card, AdminReadWriteAc, sizeof(AdminReadWriteAc)); LOG_TEST_RET(card->ctx, r, "gids to create the file AdminReadWriteAc"); //admin key r = gids_initialize_create_file(card, AdminKey, sizeof(AdminKey)); LOG_TEST_RET(card->ctx, r, "gids to create the file AdminKey"); r = gids_set_administrator_key(card, param->init_code); 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)); LOG_TEST_RET(card->ctx, r, "gids unable to save the masterfile"); r = gids_put_DO(card, CARDAPPS_FI, CARDAPPS_DO, cardapps, sizeof(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)); LOG_TEST_RET(card->ctx, r, "gids unable to save the cardcf"); r = gids_put_DO(card, CMAP_FI, CMAP_DO, NULL, 0); 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 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)); 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); apdu.lc = 2; apdu.data = (const unsigned char *) "\x3F\xFF"; apdu.datalen = 2; r = sc_transmit_apdu(card, &apdu); 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); 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 // this is a 3DES authentication with a secret key // the card mechanism is described in the GIDS specification and the computer side on the minidriver specification // the minidriver specification is incorrect because it is not ECB but CBC // then the GIDS specification is incorrect because the z1 key should be 8 bytes instead of 7 // this data comes from the reverse of the GIDS minidriver. static int gids_authenticate_admin(sc_card_t *card, u8* key) { #ifndef ENABLE_OPENSSL LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); #else EVP_CIPHER_CTX *ctx = NULL; int r; u8 apduSetRandom[20] = {0x7C,0x12,0x81,0x10,0}; u8* randomR1 = apduSetRandom + 4; u8 apduSetRandomResponse[256]; u8* randomR2 = apduSetRandomResponse+4; u8 apduSendReponse[40 + 4] = {0x7C,0x2A,0x82,0x28}; 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; EVP_CIPHER *cipher; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); // select the admin key sc_format_apdu(card, &apdu, SC_APDU_CASE_3, INS_MANAGE_SECURITY_ENVIRONMENT, 0xC1, 0xA4); apdu.lc = 3; apdu.data = (const unsigned char *) "\x83\x01\x80"; apdu.datalen = 3; r = sc_transmit_apdu(card, &apdu); 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 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 sc_format_apdu(card, &apdu, SC_APDU_CASE_4, INS_GENERAL_AUTHENTICATE, 0x00, 0x00); apdu.lc = sizeof(apduSetRandom); apdu.data = apduSetRandom; apdu.datalen = sizeof(apduSetRandom); apdu.resp = apduSetRandomResponse; apdu.resplen = sizeof(apduSetRandomResponse); apdu.le = 256; r = sc_transmit_apdu(card, &apdu); 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 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); memcpy(buffer+16, randomR1, 16); memcpy(buffer+32, z1, sizeof(z1)); // init crypto ctx = EVP_CIPHER_CTX_new(); if (ctx == NULL) { 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_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_evp_cipher_free(cipher); LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); } if (!EVP_EncryptFinal(ctx, buffer2 + buffer2size, &buffer2size)) { sc_log_openssl(card->ctx); EVP_CIPHER_CTX_free(ctx); 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); apdu.data = apduSendReponse; apdu.datalen = sizeof(apduSendReponse); apdu.resp = apduSendResponseResponse; apdu.resplen = sizeof(apduSendResponseResponse); apdu.le = 256; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); LOG_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2), "invalid return"); 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) { LOG_FUNC_CALLED(card->ctx); switch (cmd) { case SC_CARDCTL_GET_SERIALNR: return gids_get_serialnr(card, (sc_serial_number_t *) ptr); case SC_CARDCTL_GIDS_GET_ALL_CONTAINERS: return gids_get_all_containers(card, (size_t*) ptr); case SC_CARDCTL_GIDS_GET_CONTAINER_DETAIL: return gids_get_container_detail(card, (sc_cardctl_gids_get_container_t*) ptr); case SC_CARDCTL_GIDS_SELECT_KEY_REFERENCE: return gids_select_key_reference(card, (sc_pkcs15_prkey_info_t*) ptr); case SC_CARDCTL_GIDS_CREATE_KEY: return gids_create_keyfile(card, (sc_pkcs15_object_t*) ptr); case SC_CARDCTL_GIDS_GENERATE_KEY: return gids_generate_key(card, ((struct sc_cardctl_gids_genkey*) ptr)->object, ((struct sc_cardctl_gids_genkey*) ptr)->pubkey); case SC_CARDCTL_GIDS_IMPORT_KEY: return gids_import_key(card, ((struct sc_cardctl_gids_importkey*) ptr)->object, ((struct sc_cardctl_gids_importkey*) ptr)->key); case SC_CARDCTL_GIDS_SAVE_CERT: return gids_save_certificate(card, ((struct sc_cardctl_gids_save_cert*) ptr)->certobject, ((struct sc_cardctl_gids_save_cert*) ptr)->privkeyobject, ((struct sc_cardctl_gids_save_cert*) ptr)->path); case SC_CARDCTL_GIDS_DELETE_CERT: return gids_delete_cert(card, (sc_pkcs15_object_t*) ptr); case SC_CARDCTL_GIDS_DELETE_KEY: return gids_delete_key(card, (sc_pkcs15_object_t*) ptr); case SC_CARDCTL_GIDS_INITIALIZE: return gids_initialize(card, (sc_cardctl_gids_init_param_t*) ptr); case SC_CARDCTL_GIDS_SET_ADMIN_KEY: return gids_set_administrator_key(card, (u8*) ptr); case SC_CARDCTL_GIDS_AUTHENTICATE_ADMIN: return gids_authenticate_admin(card, (u8*) ptr); default: return SC_ERROR_NOT_SUPPORTED; } } 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; gids_ops.read_binary = gids_read_binary; gids_ops.write_binary = NULL; gids_ops.update_binary = NULL; gids_ops.erase_binary = NULL; gids_ops.read_record = NULL; gids_ops.write_record = NULL; gids_ops.append_record = NULL; gids_ops.update_record = NULL; gids_ops.select_file = gids_select_file; gids_ops.get_response = iso_ops->get_response; gids_ops.get_challenge = NULL; gids_ops.verify = NULL; // see pin_cmd gids_ops.logout = gids_logout; gids_ops.restore_security_env = NULL; gids_ops.set_security_env = gids_set_security_env; 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 gids_ops.create_file = iso_ops->create_file; gids_ops.delete_file = NULL; gids_ops.list_files = NULL; gids_ops.check_sw = iso_ops->check_sw; gids_ops.card_ctl = gids_card_ctl; gids_ops.process_fci = iso_ops->process_fci; gids_ops.construct_fci = iso_ops->construct_fci; gids_ops.pin_cmd = gids_pin_cmd; gids_ops.get_data = NULL; 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; } struct sc_card_driver *sc_get_gids_driver(void) { return sc_get_driver(); } #else struct sc_card_driver *sc_get_gids_driver(void) { return NULL; } #endif OpenSC-0.26.1/src/libopensc/card-gids.h000066400000000000000000000060621474147347300175240ustar00rootroot00000000000000/* * card-gids.h: Support for GIDS smart cards. * * Copyright (C) 2015 Vincent Le Toux (My Smart Logon) * * 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_GIDS_H_ #define CARD_GIDS_H_ #ifdef __cplusplus extern "C" { #endif #include "pkcs15.h" struct sc_cardctl_gids_genkey { sc_pkcs15_object_t *object; struct sc_pkcs15_pubkey* pubkey; }; struct sc_cardctl_gids_importkey { sc_pkcs15_object_t *object; sc_pkcs15_prkey_t *key; }; struct sc_cardctl_gids_save_cert { sc_pkcs15_object_t *certobject; sc_pkcs15_object_t *privkeyobject; struct sc_path *path; }; typedef struct sc_cardctl_gids_get_container { size_t containernum; int pubusage; int prvusage; char label[40]; int module_length; sc_path_t certificatepath; } sc_cardctl_gids_get_container_t; typedef struct sc_cardctl_gids_init_param { u8 init_code[24]; size_t user_pin_len; u8* user_pin; u8 cardid[16]; } sc_cardctl_gids_init_param_t; // information about common files #define UserCreateDeleteDirAc_FI 0xA000 #define EveryoneReadUserWriteAc_FI 0xA010 #define EveryoneReadAdminWriteAc_FI 0xA012 #define MF_FI UserCreateDeleteDirAc_FI #define MF_DO 0xDF1F #define CERT_FI EveryoneReadUserWriteAc_FI #define KEYMAP_FI UserCreateDeleteDirAc_FI #define KEYMAP_DO 0xDF20 #define CARDAPPS_FI EveryoneReadUserWriteAc_FI #define CARDAPPS_DO 0xDF21 #define CARDCF_FI EveryoneReadUserWriteAc_FI #define CARDCF_DO 0xDF22 #define CMAP_FI EveryoneReadUserWriteAc_FI #define CMAP_DO 0xDF23 #define CARDID_FI EveryoneReadAdminWriteAc_FI #define CARDID_DO 0xDF20 #define GIDS_MAX_DO 0xDFFF #define MAX_GIDS_FILE_SIZE 65000 typedef struct gids_mf_record { char directory[9]; char filename[9]; int dataObjectIdentifier; int fileIdentifier; } gids_mf_record_t; struct gids_keymap_record { int state; unsigned char algid; unsigned char keytype; unsigned short keyref; unsigned short unknownWithFFFF; }; #define GIDS_MAX_CONTAINER 126 // stolen from cardmod.h #define MAX_CONTAINER_NAME_LEN 39 #define CONTAINER_MAP_VALID_CONTAINER 1 #define CONTAINER_MAP_DEFAULT_CONTAINER 2 typedef struct _CONTAINER_MAP_RECORD { unsigned short wszGuid [MAX_CONTAINER_NAME_LEN + 1]; unsigned char bFlags; unsigned char bReserved; unsigned short wSigKeySizeBits; unsigned short wKeyExchangeKeySizeBits; } CONTAINER_MAP_RECORD, *PCONTAINER_MAP_RECORD; #ifdef __cplusplus } #endif #endif /* CARD_GIDS_H_ */ OpenSC-0.26.1/src/libopensc/card-iasecc.c000066400000000000000000003371761474147347300200350ustar00rootroot00000000000000/* * card-iasecc.c: Support for IAS/ECC smart cards * * Copyright (C) 2010 Viktor Tarasov * OpenTrust * * 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 #ifdef ENABLE_OPENSSL /* empty file without openssl */ #include #include #include #include #include #include #include #include #include #include #include #include "internal.h" #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" #define IASECC_CARD_DEFAULT_FLAGS ( 0 \ | SC_ALGORITHM_ONBOARD_KEY_GEN \ | SC_ALGORITHM_RSA_PAD_ISO9796 \ | SC_ALGORITHM_RSA_PAD_PKCS1 \ | SC_ALGORITHM_RSA_HASH_NONE \ | 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; /* our operations table with overrides */ static struct sc_card_operations iasecc_ops; static struct sc_card_driver iasecc_drv = { "IAS-ECC", "iasecc", &iasecc_ops, NULL, 0, NULL }; 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: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 }, { "3B:7F:18:00:00:00:31:B8:64:50:23:EC:C1:73:94:01:80:82:90:00", NULL, "IAS/ECC v1.0.1 Sagem ypsID S3", SC_CARD_TYPE_IASECC_SAGEM, 0, NULL }, { "3B:DF:96:00:80:31:FE:45:00:31:B8:64:04:1F:EC:C1:73:94:01:80:82:90:00:EC", NULL, "IAS/ECC Morpho MinInt - Agent Card", SC_CARD_TYPE_IASECC_MI, 0, NULL }, { "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", NULL, "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:02:04:03:55:00:02:34", NULL, "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 OberthurIASECC_AID = { {0xA0,0x00,0x00,0x00,0x77,0x01,0x08,0x00,0x07,0x00,0x00,0xFE,0x00,0x00,0x01,0x00}, 16 }; static struct sc_aid MIIASECC_AID = { { 0x4D, 0x49, 0x4F, 0x4D, 0x43, 0x54}, 6 }; struct iasecc_pin_status { unsigned char sha1[SHA_DIGEST_LENGTH]; unsigned char reference; struct iasecc_pin_status *next; struct iasecc_pin_status *prev; }; 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, struct iasecc_pin_policy *pin); static int iasecc_pin_get_status(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_left); static int iasecc_pin_get_info(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_left); 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); #ifdef ENABLE_SM static int _iasecc_sm_read_binary(struct sc_card *card, unsigned int offs, unsigned char *buf, size_t count); static int _iasecc_sm_update_binary(struct sc_card *card, unsigned int offs, const unsigned char *buff, size_t count); #endif static int iasecc_chv_cache_verified(struct sc_card *card, struct sc_pin_cmd_data *pin_cmd) { struct sc_context *ctx = card->ctx; struct iasecc_pin_status *pin_status = NULL, *current = NULL; LOG_FUNC_CALLED(ctx); for(current = checked_pins; current; current = current->next) if (current->reference == pin_cmd->pin_reference) break; if (current) { sc_log(ctx, "iasecc_chv_cache_verified() current PIN-%i", current->reference); pin_status = current; } else { pin_status = calloc(1, sizeof(struct iasecc_pin_status)); if (!pin_status) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot callocate PIN status info"); sc_log(ctx, "iasecc_chv_cache_verified() allocated %p", pin_status); } pin_status->reference = pin_cmd->pin_reference; if (pin_cmd->pin1.data) SHA1(pin_cmd->pin1.data, pin_cmd->pin1.len, pin_status->sha1); else memset(pin_status->sha1, 0, SHA_DIGEST_LENGTH); sc_log_hex(ctx, "iasecc_chv_cache_verified() sha1(PIN)", pin_status->sha1, SHA_DIGEST_LENGTH); if (!current) { if (!checked_pins) { checked_pins = pin_status; } else { checked_pins->prev = pin_status; pin_status->next = checked_pins; checked_pins = pin_status; } } LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int iasecc_chv_cache_clean(struct sc_card *card, struct sc_pin_cmd_data *pin_cmd) { struct sc_context *ctx = card->ctx; struct iasecc_pin_status *current = NULL; LOG_FUNC_CALLED(ctx); for(current = checked_pins; current; current = current->next) if (current->reference == pin_cmd->pin_reference) break; if (!current) LOG_FUNC_RETURN(ctx, SC_SUCCESS); if (current->next && current->prev) { current->prev->next = current->next; current->next->prev = current->prev; } else if (!current->prev) { checked_pins = current->next; } else if (!current->next && current->prev) { current->prev->next = NULL; } free(current); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static struct iasecc_pin_status * iasecc_chv_cache_is_verified(struct sc_card *card, struct sc_pin_cmd_data *pin_cmd) { struct sc_context *ctx = card->ctx; struct iasecc_pin_status *current = NULL; unsigned char data_sha1[SHA_DIGEST_LENGTH]; LOG_FUNC_CALLED(ctx); if (pin_cmd->pin1.data) SHA1(pin_cmd->pin1.data, pin_cmd->pin1.len, data_sha1); else memset(data_sha1, 0, 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) break; if (current && !memcmp(data_sha1, current->sha1, SHA_DIGEST_LENGTH)) { sc_log(ctx, "PIN-%i status 'verified'", pin_cmd->pin_reference); return current; } sc_log(ctx, "PIN-%i status 'not verified'", pin_cmd->pin_reference); return NULL; } static int iasecc_select_mf(struct sc_card *card, struct sc_file **file_out) { struct sc_context *ctx = card->ctx; struct sc_file *mf_file = NULL; struct sc_path path; int rv; LOG_FUNC_CALLED(ctx); if (file_out) *file_out = NULL; memset(&path, 0, sizeof(struct sc_path)); if (!card->ef_atr || !card->ef_atr->aid.len) { struct sc_apdu apdu; unsigned char apdu_resp[SC_MAX_APDU_BUFFER_SIZE]; /* 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, 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); LOG_TEST_RET(card->ctx, rv, "Cannot select MF"); } else { memset(&path, 0, sizeof(path)); path.type = SC_PATH_TYPE_DF_NAME; memcpy(path.value, card->ef_atr->aid.value, card->ef_atr->aid.len); path.len = card->ef_atr->aid.len; rv = iasecc_select_file(card, &path, file_out); LOG_TEST_RET(ctx, rv, "Unable to ROOT selection"); } /* 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 envisaged. */ mf_file = sc_file_new(); if (mf_file == NULL) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot allocate MF file"); 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; if (file_out && *file_out == NULL) *file_out = mf_file; else sc_file_free(mf_file); 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; LOG_FUNC_CALLED(card->ctx); /* Select application (deselect previously selected application) */ sc_format_apdu(card, &apdu, SC_APDU_CASE_4_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); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } static int iasecc_match_card(struct sc_card *card) { struct sc_context *ctx = card->ctx; int i; i = _sc_match_atr(card, iasecc_known_atrs, &card->type); if (i < 0) { sc_log(ctx, "card not matched"); return 0; } sc_log(ctx, "'%s' card matched", iasecc_known_atrs[i].name); return 1; } static int iasecc_parse_ef_atr(struct sc_card *card) { struct sc_context *ctx = card->ctx; struct iasecc_private_data *pdata = (struct iasecc_private_data *) card->drv_data; struct iasecc_version *version = &pdata->version; struct iasecc_io_buffer_sizes *sizes = &pdata->max_sizes; int rv; LOG_FUNC_CALLED(ctx); rv = sc_parse_ef_atr(card); LOG_TEST_RET(ctx, rv, "MF selection error"); if (card->ef_atr->pre_issuing_len < 4) LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Invalid pre-issuing data"); version->ic_manufacturer = card->ef_atr->pre_issuing[0]; version->ic_type = card->ef_atr->pre_issuing[1]; version->os_version = card->ef_atr->pre_issuing[2]; version->iasecc_version = card->ef_atr->pre_issuing[3]; sc_log(ctx, "EF.ATR: IC manufacturer/type %X/%X, OS/IasEcc versions %X/%X", version->ic_manufacturer, version->ic_type, version->os_version, version->iasecc_version); if (card->ef_atr->issuer_data_len < 16) LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Invalid issuer data"); sizes->send = card->ef_atr->issuer_data[2] * 0x100 + card->ef_atr->issuer_data[3]; sizes->send_sc = card->ef_atr->issuer_data[6] * 0x100 + card->ef_atr->issuer_data[7]; 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; /* Most of the card producers interpret 'send' values as "maximum APDU data size". * Oberthur strictly follows specification and interpret these values as "maximum APDU command size". * Here we need 'data size'. */ if (card->max_send_size > 0xFF) card->max_send_size -= 5; sc_log(ctx, "EF.ATR: max send/recv sizes %"SC_FORMAT_LEN_SIZE_T"X/%"SC_FORMAT_LEN_SIZE_T"X", card->max_send_size, card->max_recv_size); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int iasecc_init_gemalto(struct sc_card *card) { struct sc_context *ctx = card->ctx; struct sc_path path; unsigned int flags; int rv = 0; LOG_FUNC_CALLED(ctx); flags = IASECC_CARD_DEFAULT_FLAGS; card->caps = IASECC_CARD_DEFAULT_CAPS; sc_format_path("3F00", &path); 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); if (rv == SC_ERROR_FILE_NOT_FOUND) { sc_log(ctx, "Select MF"); rv = iasecc_select_mf(card, NULL); sc_log(ctx, "rv %i", rv); LOG_TEST_RET(ctx, rv, "MF selection error"); rv = iasecc_parse_ef_atr(card); sc_log(ctx, "rv %i", rv); } 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); } static int iasecc_oberthur_match(struct sc_card *card) { struct sc_context *ctx = card->ctx; unsigned char *hist = card->reader->atr_info.hist_bytes; LOG_FUNC_CALLED(ctx); if (*hist != 0x80 || ((*(hist+1)&0xF0) != 0xF0)) LOG_FUNC_RETURN(ctx, SC_ERROR_OBJECT_NOT_FOUND); 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); 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, OberthurIASECC_AID.value, OberthurIASECC_AID.len); card->ef_atr->aid.len = OberthurIASECC_AID.len; LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int iasecc_init_oberthur(struct sc_card *card) { struct sc_context *ctx = card->ctx; unsigned int flags; 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; iasecc_parse_ef_atr(card); /* if we fail to select CM, */ if (gp_select_card_manager(card)) { gp_select_isd_rid(card); } rv = iasecc_oberthur_match(card); LOG_TEST_RET(ctx, rv, "unknown Oberthur's IAS/ECC card"); rv = iasecc_select_mf(card, NULL); LOG_TEST_RET(ctx, rv, "MF selection error"); rv = iasecc_parse_ef_atr(card); LOG_TEST_RET(ctx, rv, "EF.ATR read or parse error"); sc_log(ctx, "EF.ATR(aid:'%s')", sc_dump_hex(card->ef_atr->aid.value, card->ef_atr->aid.len)); LOG_FUNC_RETURN(ctx, rv); } static int iasecc_mi_match(struct sc_card *card) { struct sc_context *ctx = card->ctx; unsigned char resp[0x100]; size_t resp_len; int rv = 0; LOG_FUNC_CALLED(ctx); resp_len = sizeof(resp); rv = iasecc_select_aid(card, &MIIASECC_AID, resp, &resp_len); LOG_TEST_RET(ctx, rv, "IASECC: failed to select MI IAS/ECC applet"); 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; LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int iasecc_init_amos_or_sagem(struct sc_card *card) { struct sc_context *ctx = card->ctx; unsigned int flags; 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; 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) { rv = iasecc_select_mf(card, NULL); LOG_TEST_RET(ctx, rv, "MF selection error"); rv = iasecc_parse_ef_atr(card); } LOG_TEST_RET(ctx, rv, "IASECC: ATR parse failed"); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } inline static int iasecc_is_cpx(const struct sc_card *card) { switch(card->type) { case SC_CARD_TYPE_IASECC_CPX: case SC_CARD_TYPE_IASECC_CPXCL: return 1; default: return 0; } return 0; } static int iasecc_init_cpx(struct sc_card *card) { struct sc_context *ctx = card->ctx; unsigned int flags; int rv = 0; LOG_FUNC_CALLED(ctx); card->caps = IASECC_CARD_DEFAULT_CAPS; flags = IASECC_CARD_DEFAULT_FLAGS; _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_parse_ef_atr(card); if (rv) sc_invalidate_cache(card); /* avoid memory leakage */ 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)); if (private_data == NULL) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); card->cla = 0x00; card->drv_data = private_data; if (card->type == SC_CARD_TYPE_IASECC_GEMALTO) rv = iasecc_init_gemalto(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_amos_or_sagem(card); else if (card->type == SC_CARD_TYPE_IASECC_AMOS) rv = iasecc_init_amos_or_sagem(card); else if (card->type == SC_CARD_TYPE_IASECC_MI) 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) { if (card->ef_atr && card->ef_atr->aid.len) { struct sc_path path; memset(&path, 0, sizeof(struct sc_path)); path.type = SC_PATH_TYPE_DF_NAME; memcpy(path.value, card->ef_atr->aid.value, card->ef_atr->aid.len); path.len = card->ef_atr->aid.len; rv = iasecc_select_file(card, &path, NULL); sc_log(ctx, "Select ECC ROOT with the AID from EF.ATR: rv %i", rv); LOG_TEST_GOTO_ERR(ctx, rv, "Select EF.ATR AID failed"); } iasecc_get_serialnr(card, NULL); } #ifdef ENABLE_SM card->sm_ctx.ops.read_binary = _iasecc_sm_read_binary; card->sm_ctx.ops.update_binary = _iasecc_sm_update_binary; #endif 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) { 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) { struct sc_context *ctx = card->ctx; struct sc_apdu apdu; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "iasecc_read_binary(card:%p) offs %i; count %"SC_FORMAT_LEN_SIZE_T"u", card, offs, count); if (offs > 0x7fff) { sc_log(ctx, "invalid EF offset: 0x%X > 0x7FFF", offs); return SC_ERROR_OFFSET_TOO_LARGE; } sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xB0, (offs >> 8) & 0x7F, offs & 0xFF); apdu.le = count < 0x100 ? count : 0x100; apdu.resplen = count; apdu.resp = buf; 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, "iasecc_read_binary() failed"); sc_log(ctx, "iasecc_read_binary() apdu.resplen %"SC_FORMAT_LEN_SIZE_T"u", apdu.resplen); if (apdu.resplen == IASECC_READ_BINARY_LENGTH_MAX && apdu.resplen < count) { 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, (int)apdu.resplen); } static int iasecc_erase_binary(struct sc_card *card, unsigned int offs, size_t count, unsigned long flags) { struct sc_context *ctx = card->ctx; unsigned char *tmp = NULL; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "iasecc_erase_binary(card:%p) count %"SC_FORMAT_LEN_SIZE_T"u", card, count); if (!count) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "'ERASE BINARY' failed: invalid size to erase"); tmp = malloc(count); if (!tmp) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot allocate temporary buffer"); memset(tmp, 0xFF, count); rv = sc_update_binary(card, offs, tmp, count, flags); free(tmp); LOG_FUNC_RETURN(ctx, rv); } #if ENABLE_SM static int _iasecc_sm_read_binary(struct sc_card *card, unsigned int offs, unsigned char *buff, size_t count) { struct sc_context *ctx = card->ctx; const struct sc_acl_entry *entry = NULL; int rv; 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); if (offs > 0x7fff) LOG_TEST_RET(ctx, SC_ERROR_OFFSET_TOO_LARGE, "Invalid arguments"); 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 (!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->key_ref & IASECC_SCB_METHOD_MASK_REF; rv = iasecc_sm_read_binary(card, se_num, offs, buff, count); LOG_FUNC_RETURN(ctx, rv); } } LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int _iasecc_sm_update_binary(struct sc_card *card, unsigned int offs, const unsigned char *buff, size_t count) { struct sc_context *ctx = card->ctx; const struct sc_acl_entry *entry = NULL; int rv; if (count == 0) return SC_SUCCESS; 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); if (card->cache.valid && card->cache.current_ef) { entry = sc_file_get_acl_entry(card->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->key_ref & IASECC_SCB_METHOD_MASK_REF; rv = iasecc_sm_update_binary(card, se_num, offs, buff, count); LOG_FUNC_RETURN(ctx, rv); } } LOG_FUNC_RETURN(ctx, 0); } #endif static int iasecc_emulate_fcp(struct sc_context *ctx, struct sc_apdu *apdu) { unsigned char dummy_df_fcp[] = { 0x62,0xFF, 0x82,0x01,0x38, 0x8A,0x01,0x05, 0xA1,0x04,0x8C,0x02,0x02,0x00, 0x84,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF }; LOG_FUNC_CALLED(ctx); if (apdu->p1 != 0x04) LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "FCP emulation supported only for the DF-NAME selection type"); if (apdu->datalen > 16) LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Invalid DF-NAME length"); if (apdu->resplen < apdu->datalen + 16) LOG_TEST_RET(ctx, SC_ERROR_BUFFER_TOO_SMALL, "not enough space for FCP data"); 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); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } /* TODO: redesign using of cache * 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 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); memcpy(&lpath, path, sizeof(struct sc_path)); if (file_out) *file_out = NULL; sc_log(ctx, "iasecc_select_file(card:%p) path.len %"SC_FORMAT_LEN_SIZE_T"u; path.type %i; aid_len %"SC_FORMAT_LEN_SIZE_T"u", 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 ((!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"); memmove(&lpath.value[0], &lpath.value[2], lpath.len - 2); lpath.len -= 2; } if (lpath.aid.len) { struct sc_file *file = NULL; struct sc_path ppath; sc_log(ctx, "iasecc_select_file() select parent AID:%p/%"SC_FORMAT_LEN_SIZE_T"u", lpath.aid.value, lpath.aid.len); sc_log(ctx, "iasecc_select_file() select parent AID:%s", sc_dump_hex(lpath.aid.value, lpath.aid.len)); memset(&ppath, 0, sizeof(ppath)); memcpy(ppath.value, lpath.aid.value, lpath.aid.len); 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_GOTO_ERR(ctx, rv, "select AID path failed"); if (file_out) { sc_file_free(*file_out); *file_out = file; } else { sc_file_free(file); } if (lpath.type == SC_PATH_TYPE_DF_NAME) lpath.type = SC_PATH_TYPE_FROM_CURRENT; } if (lpath.type == SC_PATH_TYPE_PATH) lpath.type = SC_PATH_TYPE_FROM_CURRENT; if (!lpath.len) { if (file_out) { sc_file_free(*file_out); *file_out = NULL; } 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) { sc_file_free(*file_out); sc_file_dup(file_out, card->cache.current_df); } sc_print_cache(card); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } do { struct sc_apdu apdu; struct sc_file *file = NULL; unsigned char rbuf[SC_MAX_APDU_BUFFER_SIZE]; size_t pathlen = lpath.len; sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0x00, 0x00); if (card->type != SC_CARD_TYPE_IASECC_GEMALTO && 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 && 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) 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; } } else if (lpath.type == SC_PATH_TYPE_FROM_CURRENT) { apdu.p1 = 0x09; 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; pathlen = 0; apdu.cse = SC_APDU_CASE_2_SHORT; } else if (lpath.type == SC_PATH_TYPE_DF_NAME) { apdu.p1 = 0x04; 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); rv = SC_ERROR_NOT_SUPPORTED; LOG_TEST_GOTO_ERR(ctx, rv, "iasecc_select_file() invalid PATH type"); } for (ii=0; ii<2; ii++) { apdu.lc = pathlen; apdu.data = lpath.value; apdu.datalen = pathlen; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = 256; rv = sc_transmit_apdu(card, &apdu); 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; } if (ii) { /* 'SELECT AID' do not returned FCP. Try to emulate. */ apdu.resplen = sizeof(rbuf); rv = iasecc_emulate_fcp(ctx, &apdu); 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) { sc_invalidate_cache(card); 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_GOTO_ERR(ctx, rv, "iasecc_select_file() check SW failed"); sc_log(ctx, "iasecc_select_file() apdu.resp %"SC_FORMAT_LEN_SIZE_T"u", apdu.resplen); if (apdu.resplen) { sc_log(ctx, "apdu.resp %02X:%02X:%02X...", apdu.resp[0], apdu.resp[1], apdu.resp[2]); switch (apdu.resp[0]) { case 0x62: case 0x6F: file = sc_file_new(); 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) { 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; sc_file_dup(&card->cache.current_df, file); card->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); card->cache.valid = 1; } if (file_out) { sc_file_free(*file_out); *file_out = file; } else { sc_file_free(file); } } 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; } } 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; } static int 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, offs, ii; int rv; const unsigned char *acls = NULL, *tag = NULL; unsigned char mask; unsigned char ops_DF[7] = { SC_AC_OP_DELETE, 0xFF, SC_AC_OP_ACTIVATE, SC_AC_OP_DEACTIVATE, 0xFF, SC_AC_OP_CREATE, 0xFF }; unsigned char ops_EF[7] = { SC_AC_OP_DELETE, 0xFF, SC_AC_OP_ACTIVATE, SC_AC_OP_DEACTIVATE, 0xFF, SC_AC_OP_UPDATE, SC_AC_OP_READ }; LOG_FUNC_CALLED(ctx); tag = sc_asn1_find_tag(ctx, buf, buflen, 0x6F, &taglen); sc_log(ctx, "processing FCI: 0x6F tag %p", tag); if (tag != NULL) { sc_log(ctx, " FCP length %"SC_FORMAT_LEN_SIZE_T"u", taglen); buf = tag; buflen = taglen; } tag = sc_asn1_find_tag(ctx, buf, buflen, 0x62, &taglen); sc_log(ctx, "processing FCI: 0x62 tag %p", tag); if (tag != NULL) { sc_log(ctx, " FCP length %"SC_FORMAT_LEN_SIZE_T"u", taglen); buf = tag; buflen = taglen; } rv = iso_ops->process_fci(card, file, buf, buflen); LOG_TEST_RET(ctx, rv, "ISO parse FCI failed"); /* Gemalto: 6F 19 80 02 02 ED 82 01 01 83 02 B0 01 88 00 8C 07 7B 17 17 17 17 17 00 8A 01 05 90 00 Sagem: 6F 17 62 15 80 02 00 7D 82 01 01 8C 02 01 00 83 02 2F 00 88 01 F0 8A 01 05 90 00 Oberthur: 62 1B 80 02 05 DC 82 01 01 83 02 B0 01 88 00 A1 09 8C 07 7B 17 FF 17 17 17 00 8A 01 05 90 00 */ sc_log(ctx, "iasecc_process_fci() type %i; let's parse file ACLs", file->type); tag = sc_asn1_find_tag(ctx, buf, buflen, IASECC_DOCP_TAG_ACLS, &taglen); if (tag) acls = sc_asn1_find_tag(ctx, tag, taglen, IASECC_DOCP_TAG_ACLS_CONTACT, &taglen); else acls = sc_asn1_find_tag(ctx, buf, buflen, IASECC_DOCP_TAG_ACLS_CONTACT, &taglen); if (!acls) { sc_log(ctx, "ACLs not found in data(%"SC_FORMAT_LEN_SIZE_T"u) %s", buflen, sc_dump_hex(buf, buflen)); LOG_TEST_RET(ctx, SC_ERROR_OBJECT_NOT_FOUND, "ACLs tag missing"); } sc_log(ctx, "ACLs(%"SC_FORMAT_LEN_SIZE_T"u) '%s'", taglen, sc_dump_hex(acls, taglen)); mask = 0x40, offs = 1; 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 %"SC_FORMAT_LEN_SIZE_T"u, op 0x%X, acls[offs] 0x%X", mask, offs, op, acls[offs]); if (op == 0xFF) { ; } else if (acls[offs] == 0) { sc_file_add_acl_entry(file, op, SC_AC_NONE, 0); } else if (acls[offs] == 0xFF) { sc_file_add_acl_entry(file, op, SC_AC_NEVER, 0); } else if ((acls[offs] & IASECC_SCB_METHOD_MASK) == IASECC_SCB_METHOD_USER_AUTH) { sc_file_add_acl_entry(file, op, SC_AC_SEN, acls[offs] & IASECC_SCB_METHOD_MASK_REF); } else if (acls[offs] & IASECC_SCB_METHOD_MASK) { sc_file_add_acl_entry(file, op, SC_AC_SCB, acls[offs]); } else { sc_log(ctx, "Warning: non supported SCB method: %X", acls[offs]); sc_file_add_acl_entry(file, op, SC_AC_NEVER, 0); } offs++; } LOG_FUNC_RETURN(ctx, 0); } static int iasecc_fcp_encode(struct sc_card *card, struct sc_file *file, unsigned char *out, size_t out_len) { struct sc_context *ctx = card->ctx; unsigned char buf[0x80], type; unsigned char ops[7] = { 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, amb, offs = 0, mask, nn_smb; LOG_FUNC_CALLED(ctx); if (file->type == SC_FILE_TYPE_DF) type = IASECC_FCP_TYPE_DF; else type = IASECC_FCP_TYPE_EF; buf[offs++] = IASECC_FCP_TAG_SIZE; buf[offs++] = 2; buf[offs++] = (file->size >> 8) & 0xFF; buf[offs++] = file->size & 0xFF; buf[offs++] = IASECC_FCP_TAG_TYPE; buf[offs++] = 1; buf[offs++] = type; buf[offs++] = IASECC_FCP_TAG_FID; buf[offs++] = 2; buf[offs++] = (file->id >> 8) & 0xFF; buf[offs++] = file->id & 0xFF; buf[offs++] = IASECC_FCP_TAG_SFID; buf[offs++] = 0; amb = 0, mask = 0x40, nn_smb = 0; for (ii = 0; ii < sizeof(ops); ii++, mask >>= 1) { const struct sc_acl_entry *entry; if (ops[ii]==0xFF) continue; entry = sc_file_get_acl_entry(file, ops[ii]); if (!entry) continue; sc_log(ctx, "method %X; reference %X", entry->method, entry->key_ref); if (entry->method == SC_AC_NEVER) continue; else if (entry->method == SC_AC_NONE) smbs[nn_smb++] = 0x00; else if (entry->method == SC_AC_CHV) smbs[nn_smb++] = entry->key_ref | IASECC_SCB_METHOD_USER_AUTH; else if (entry->method == SC_AC_SEN) smbs[nn_smb++] = entry->key_ref | IASECC_SCB_METHOD_USER_AUTH; else if (entry->method == SC_AC_SCB) smbs[nn_smb++] = entry->key_ref; else if (entry->method == SC_AC_PRO) smbs[nn_smb++] = entry->key_ref | IASECC_SCB_METHOD_SM; else LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Non supported AC method"); amb |= mask; sc_log(ctx, "%"SC_FORMAT_LEN_SIZE_T"u: AMB %"SC_FORMAT_LEN_SIZE_T"X; nn_smb %"SC_FORMAT_LEN_SIZE_T"u", ii, amb, nn_smb); } /* TODO: Encode contactless ACLs and life cycle status for all IAS/ECC cards */ if (card->type == SC_CARD_TYPE_IASECC_SAGEM || card->type == SC_CARD_TYPE_IASECC_AMOS ) { unsigned char status = 0; buf[offs++] = IASECC_FCP_TAG_ACLS; buf[offs++] = 2*(2 + 1 + nn_smb); buf[offs++] = IASECC_FCP_TAG_ACLS_CONTACT; buf[offs++] = nn_smb + 1; buf[offs++] = amb; memcpy(buf + offs, smbs, nn_smb); offs += nn_smb; /* Same ACLs for contactless */ buf[offs++] = IASECC_FCP_TAG_ACLS_CONTACTLESS; buf[offs++] = nn_smb + 1; buf[offs++] = amb; memcpy(buf + offs, smbs, nn_smb); offs += nn_smb; if (file->status == SC_FILE_STATUS_ACTIVATED) status = 0x05; else if (file->status == SC_FILE_STATUS_CREATION) status = 0x01; if (status) { buf[offs++] = 0x8A; buf[offs++] = 0x01; buf[offs++] = status; } } else { buf[offs++] = IASECC_FCP_TAG_ACLS; buf[offs++] = 2 + 1 + nn_smb; buf[offs++] = IASECC_FCP_TAG_ACLS_CONTACT; buf[offs++] = nn_smb + 1; buf[offs++] = amb; memcpy(buf + offs, smbs, nn_smb); offs += nn_smb; } if (out) { if (out_len < offs) LOG_TEST_RET(ctx, SC_ERROR_BUFFER_TOO_SMALL, "Buffer too small to encode FCP"); memcpy(out, buf, offs); } LOG_FUNC_RETURN(ctx, (int)offs); } static int iasecc_create_file(struct sc_card *card, struct sc_file *file) { struct sc_context *ctx = card->ctx; struct sc_apdu apdu; const struct sc_acl_entry *entry = NULL; unsigned char sbuf[0x100]; size_t sbuf_len; 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"); 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 (!entry) LOG_TEST_RET(ctx, SC_ERROR_OBJECT_NOT_FOUND, "iasecc_create_file() 'CREATE' ACL not present"); sc_log(ctx, "iasecc_create_file() 'CREATE' method/reference %X/%X", entry->method, entry->key_ref); sc_log(ctx, "iasecc_create_file() create data: '%s'", sc_dump_hex(sbuf, sbuf_len + 2)); if (entry->method == SC_AC_SCB && (entry->key_ref & IASECC_SCB_METHOD_SM)) { rv = iasecc_sm_create_file(card, entry->key_ref & IASECC_SCB_METHOD_MASK_REF, sbuf, sbuf_len + 2); LOG_TEST_RET(ctx, rv, "iasecc_create_file() SM create file error"); rv = iasecc_select_file(card, &file->path, NULL); LOG_FUNC_RETURN(ctx, rv); } } sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, 0, 0); apdu.data = sbuf; apdu.datalen = sbuf_len + 2; apdu.lc = sbuf_len + 2; 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, "iasecc_create_file() create file error"); rv = iasecc_select_file(card, &file->path, NULL); LOG_TEST_RET(ctx, rv, "Cannot select newly created 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) { struct sc_context *ctx = card->ctx; struct sc_path path; int rv; LOG_FUNC_CALLED(ctx); if (!card->ef_atr || !card->ef_atr->aid.len) return SC_SUCCESS; memset(&path, 0, sizeof(struct sc_path)); path.type = SC_PATH_TYPE_DF_NAME; memcpy(path.value, card->ef_atr->aid.value, card->ef_atr->aid.len); path.len = card->ef_atr->aid.len; rv = iasecc_select_file(card, &path, NULL); sc_log(ctx, "Select ECC ROOT with the AID from EF.ATR: rv %i", rv); LOG_FUNC_RETURN(ctx, rv); } static int iasecc_finish(struct sc_card *card) { struct sc_context *ctx = card->ctx; struct iasecc_private_data *private_data = (struct iasecc_private_data *)card->drv_data; struct iasecc_se_info *se_info = private_data->se_info, *next; LOG_FUNC_CALLED(ctx); while (se_info) { sc_file_free(se_info->df); next = se_info->next; free(se_info); se_info = next; } free(card->drv_data); card->drv_data = NULL; LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int iasecc_delete_file(struct sc_card *card, const struct sc_path *path) { struct sc_context *ctx = card->ctx; const struct sc_acl_entry *entry = NULL; struct sc_apdu apdu; struct sc_file *file = NULL; int rv; LOG_FUNC_CALLED(ctx); sc_print_cache(card); rv = iasecc_select_file(card, path, &file); if (rv == SC_ERROR_FILE_NOT_FOUND) LOG_FUNC_RETURN(ctx, SC_SUCCESS); LOG_TEST_RET(ctx, rv, "Cannot select file to delete"); entry = sc_file_get_acl_entry(file, SC_AC_OP_DELETE); 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->key_ref & IASECC_SCB_METHOD_MASK_REF; rv = iasecc_sm_delete_file(card, se_num, file->id); sc_file_free(file); } else { sc_file_free(file); sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0xE4, 0x00, 0x00); 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, "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 iasecc_check_sw(struct sc_card *card, unsigned int sw1, unsigned int sw2) { if (sw1 == 0x62 && sw2 == 0x82) return SC_SUCCESS; return iso_ops->check_sw(card, sw1, sw2); } static unsigned iasecc_get_algorithm(struct sc_context *ctx, const struct sc_security_env *env, unsigned operation, unsigned mechanism) { const struct sc_supported_algo_info *info = NULL; int ii; if (!env) return 0; for (ii=0;iisupported_algos[ii].reference; ii++) if ((env->supported_algos[ii].operations & operation) && (env->supported_algos[ii].mechanism == mechanism)) break; if (ii < SC_MAX_SUPPORTED_ALGORITHMS && env->supported_algos[ii].reference) { info = &env->supported_algos[ii]; sc_log(ctx, "found IAS/ECC algorithm %X:%X:%X:%X", info->reference, info->mechanism, info->operations, info->algo_ref); } else { sc_log(ctx, "cannot find IAS/ECC algorithm (operation:%X,mechanism:%X)", operation, mechanism); } return info ? info->algo_ref : 0; } 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 sc_context *ctx = card->ctx; struct iasecc_se_info *se_info = NULL, *si = NULL; int rv; LOG_FUNC_CALLED(ctx); se_info = calloc(1, sizeof(struct iasecc_se_info)); if (!se_info) 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 (se_info->df == NULL) { free(se_info); LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot duplicate current DF file"); } } rv = iasecc_docp_copy(ctx, &se->docp, &se_info->docp); if (rv < 0) { free(se_info->df); free(se_info); LOG_TEST_RET(ctx, rv, "Cannot make copy of DOCP"); } if (!prv->se_info) { prv->se_info = se_info; } else { for (si = prv->se_info; si->next; si = si->next) ; si->next = se_info; } LOG_FUNC_RETURN(ctx, rv); } 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 sc_context *ctx = card->ctx; struct iasecc_se_info *si = NULL; int rv; LOG_FUNC_CALLED(ctx); 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) continue; if (card->cache.valid && card->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))) continue; break; } if (!si) return SC_ERROR_OBJECT_NOT_FOUND; memcpy(se, si, sizeof(struct iasecc_se_info)); if (si->df) { sc_file_dup(&se->df, si->df); if (se->df == NULL) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot duplicate current DF file"); } rv = iasecc_docp_copy(ctx, &si->docp, &se->docp); LOG_TEST_RET(ctx, rv, "Cannot make copy of DOCP"); LOG_FUNC_RETURN(ctx, rv); } int iasecc_se_get_info(struct sc_card *card, struct iasecc_se_info *se) { struct sc_context *ctx = card->ctx; struct sc_apdu apdu; unsigned char rbuf[0x100]; unsigned char sbuf_iasecc[10] = { 0x4D, 0x08, IASECC_SDO_TEMPLATE_TAG, 0x06, IASECC_SDO_TAG_HEADER, IASECC_SDO_CLASS_SE | IASECC_OBJECT_REF_LOCAL, se->reference & 0x3F, 0x02, IASECC_SDO_CLASS_SE, 0x80 }; int rv; 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); rv = iasecc_se_get_info_from_cache(card, se); if (rv == SC_ERROR_OBJECT_NOT_FOUND) { sc_log(ctx, "No SE#%X info in cache, try to use 'GET DATA'", se->reference); sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xCB, 0x3F, 0xFF); apdu.data = sbuf_iasecc; apdu.datalen = sizeof(sbuf_iasecc); apdu.lc = apdu.datalen; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = sizeof(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, "get SE data error"); rv = iasecc_se_parse(card, apdu.resp, apdu.resplen, se); LOG_TEST_RET(ctx, rv, "cannot parse SE data"); rv = iasecc_se_cache_info(card, se); LOG_TEST_RET(ctx, rv, "failed to put SE data into cache"); } LOG_FUNC_RETURN(ctx, rv); } static int iasecc_set_security_env(struct sc_card *card, const struct sc_security_env *env, int se_num) { struct sc_context *ctx = card->ctx; struct iasecc_sdo sdo; 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; unsigned long aflags; unsigned char cse_crt_at[] = { 0x84, 0x01, 0xFF, 0x80, 0x01, IASECC_ALGORITHM_RSA_PKCS }; unsigned char cse_crt_dst[] = { 0x84, 0x01, 0xFF, 0x80, 0x01, (IASECC_ALGORITHM_RSA_PKCS | IASECC_ALGORITHM_SHA1) }; unsigned char cse_crt_ht[] = { 0x80, 0x01, IASECC_ALGORITHM_SHA1 }; unsigned char cse_crt_ct[] = { 0x84, 0x01, 0xFF, 0x80, 0x01, (IASECC_ALGORITHM_RSA_PKCS_DECRYPT | IASECC_ALGORITHM_SHA1) }; int rv, operation = env->operation; /* 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%lX, senv.algorithm_ref 0x%lX", card, env->operation, env->algorithm, env->algorithm_ref); memset(&sdo, 0, sizeof(sdo)); sdo.sdo_class = IASECC_SDO_CLASS_RSA_PRIVATE; sdo.sdo_ref = env->key_ref[0] & ~IASECC_OBJECT_REF_LOCAL; rv = iasecc_sdo_get_data(card, &sdo); LOG_TEST_RET(ctx, rv, "Cannot get RSA PRIVATE SDO 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); rv = iasecc_sdo_convert_acl(card, &sdo, SC_AC_OP_PSO_COMPUTE_SIGNATURE, &sign_meth, &sign_ref); LOG_TEST_RET(ctx, rv, "Cannot convert SC_AC_OP_SIGN acl"); rv = iasecc_sdo_convert_acl(card, &sdo, SC_AC_OP_INTERNAL_AUTHENTICATE, &auth_meth, &auth_ref); LOG_TEST_RET(ctx, rv, "Cannot convert SC_AC_OP_INT_AUTH acl"); aflags = env->algorithm_flags; 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) { if (!(aflags & (SC_ALGORITHM_RSA_HASH_SHA1 | SC_ALGORITHM_RSA_HASH_SHA256))) { sc_log(ctx, "CKM_RSA_PKCS asked -- use 'AUTHENTICATE' sign operation instead of 'SIGN'"); operation = SC_SEC_OPERATION_AUTHENTICATE; } else if (sign_meth == SC_AC_NEVER) { LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "PSO_DST not allowed for this key"); } } if (operation == SC_SEC_OPERATION_SIGN) { prv->op_method = sign_meth; prv->op_ref = sign_ref; } else if (operation == SC_SEC_OPERATION_AUTHENTICATE) { if (auth_meth == SC_AC_NEVER) LOG_TEST_RET(ctx, SC_ERROR_NOT_ALLOWED, "INTERNAL_AUTHENTICATE is not allowed for this key"); prv->op_method = auth_meth; prv->op_ref = auth_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%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_TYPE_01)) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Need RSA_PKCS1 specified"); if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA256) { algo_ref = iasecc_get_algorithm(ctx, env, SC_PKCS15_ALGO_OP_HASH, CKM_SHA256); if (!algo_ref) LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Card application do not supports HASH:SHA256"); cse_crt_ht[2] = algo_ref; /* IASECC_ALGORITHM_SHA2 */ algo_ref = iasecc_get_algorithm(ctx, env, SC_PKCS15_ALGO_OP_COMPUTE_SIGNATURE, CKM_SHA256_RSA_PKCS); if (!algo_ref) LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Card application do not supports SIGNATURE:SHA1_RSA_PKCS"); cse_crt_dst[2] = env->key_ref[0] | IASECC_OBJECT_REF_LOCAL; cse_crt_dst[5] = algo_ref; /* IASECC_ALGORITHM_RSA_PKCS | IASECC_ALGORITHM_SHA2 */ } else if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA1) { algo_ref = iasecc_get_algorithm(ctx, env, SC_PKCS15_ALGO_OP_HASH, CKM_SHA_1); if (!algo_ref) LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Card application do not supports HASH:SHA1"); cse_crt_ht[2] = algo_ref; /* IASECC_ALGORITHM_SHA1 */ algo_ref = iasecc_get_algorithm(ctx, env, SC_PKCS15_ALGO_OP_COMPUTE_SIGNATURE, CKM_SHA1_RSA_PKCS); if (!algo_ref) LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Card application do not supports SIGNATURE:SHA1_RSA_PKCS"); cse_crt_dst[2] = env->key_ref[0] | IASECC_OBJECT_REF_LOCAL; cse_crt_dst[5] = algo_ref; /* IASECC_ALGORITHM_RSA_PKCS | IASECC_ALGORITHM_SHA1 */ } else { LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Need RSA_HASH_SHA[1,256] specified"); } sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, IASECC_CRT_TAG_HT); apdu.data = cse_crt_ht; apdu.datalen = sizeof(cse_crt_ht); apdu.lc = sizeof(cse_crt_ht); 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, "MSE restore error"); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, IASECC_CRT_TAG_DST); apdu.data = cse_crt_dst; apdu.datalen = sizeof(cse_crt_dst); apdu.lc = sizeof(cse_crt_dst); break; case SC_SEC_OPERATION_AUTHENTICATE: algo_ref = iasecc_get_algorithm(ctx, env, SC_PKCS15_ALGO_OP_COMPUTE_SIGNATURE, CKM_RSA_PKCS); if (!algo_ref) LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Application do not supports SIGNATURE:RSA_PKCS"); cse_crt_at[2] = env->key_ref[0] | IASECC_OBJECT_REF_LOCAL; cse_crt_at[5] = algo_ref; /* IASECC_ALGORITHM_RSA_PKCS */ sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, IASECC_CRT_TAG_AT); apdu.data = cse_crt_at; apdu.datalen = sizeof(cse_crt_at); apdu.lc = sizeof(cse_crt_at); break; case SC_SEC_OPERATION_DECIPHER: rv = iasecc_sdo_convert_acl(card, &sdo, SC_AC_OP_PSO_DECRYPT, &prv->op_method, &prv->op_ref); 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 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 */ sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, IASECC_CRT_TAG_CT); apdu.data = cse_crt_ct; apdu.datalen = sizeof(cse_crt_ct); apdu.lc = sizeof(cse_crt_ct); break; default: LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); } 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, "MSE restore error"); prv->security_env = *env; prv->security_env.operation = operation; LOG_FUNC_RETURN(ctx, 0); } static int iasecc_chv_verify(struct sc_card *card, struct sc_pin_cmd_data *pin_cmd, unsigned char *scbs, int *tries_left) { struct sc_context *ctx = card->ctx; unsigned char scb = scbs[IASECC_ACLS_CHV_VERIFY]; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "Verify CHV PIN(ref:%i,len:%zu,scb:%X)", pin_cmd->pin_reference, pin_cmd->pin1.len, scb); if (scb & IASECC_SCB_METHOD_SM) { rv = iasecc_sm_pin_verify(card, scb & IASECC_SCB_METHOD_MASK_REF, pin_cmd, tries_left); LOG_FUNC_RETURN(ctx, rv); } rv = iso_ops->pin_cmd(card, pin_cmd, tries_left); LOG_FUNC_RETURN(ctx, rv); } static int iasecc_se_at_to_chv_reference(struct sc_card *card, unsigned reference, unsigned *chv_reference) { struct sc_context *ctx = card->ctx; struct iasecc_se_info se; struct sc_crt crt; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "SE reference %i", reference); if (reference > IASECC_SE_REF_MAX) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); memset(&se, 0, sizeof(se)); se.reference = reference; rv = iasecc_se_get_info(card, &se); LOG_TEST_RET(ctx, rv, "SDO get data error"); memset(&crt, 0, sizeof(crt)); crt.tag = IASECC_CRT_TAG_AT; crt.usage = IASECC_UQB_AT_USER_PASSWORD; rv = iasecc_se_get_crt(card, &se, &crt); LOG_TEST_RET(ctx, rv, "no authentication template for USER PASSWORD"); if (chv_reference) *chv_reference = crt.refs[0]; sc_file_free(se.df); LOG_FUNC_RETURN(ctx, rv); } static int iasecc_pin_get_status(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_left) { struct sc_context *ctx = card->ctx; struct sc_pin_cmd_data info; int rv; LOG_FUNC_CALLED(ctx); if (data->pin_type != SC_AC_CHV) LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "PIN type is not supported for status"); memset(&info, 0, sizeof(info)); info.cmd = SC_PIN_CMD_GET_INFO; info.pin_type = data->pin_type; info.pin_reference = data->pin_reference; rv = iso_ops->pin_cmd(card, &info, tries_left); LOG_TEST_RET(ctx, rv, "Failed to get PIN info"); 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, struct sc_pin_cmd_data *data, int *tries_left) { struct sc_context *ctx = card->ctx; unsigned type = data->pin_type; unsigned reference = data->pin_reference; struct sc_pin_cmd_data pin_cmd; 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:%zu,%p)", type, reference, data->pin1.len, data->pin1.data); if (type == SC_AC_AUT) { rv = iasecc_sm_external_authentication(card, reference, tries_left); LOG_FUNC_RETURN(ctx, rv); } if (type == SC_AC_SCB) { if (reference & IASECC_SCB_METHOD_USER_AUTH) { type = SC_AC_SEN; reference = reference & IASECC_SCB_METHOD_MASK_REF; } } if (type == SC_AC_SEN) { 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"); } 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 = reference; pin_cmd.cmd = SC_PIN_CMD_VERIFY; rv = iasecc_pin_get_status(card, &pin_cmd, tries_left); if (data->pin1.data && !data->pin1.len) LOG_FUNC_RETURN(ctx, rv); if (!rv) { 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_SECURITY_STATUS_NOT_SATISFIED) { LOG_FUNC_RETURN(ctx, rv); } iasecc_chv_cache_clean(card, &pin_cmd); rv = iasecc_pin_merge_policy(card, &pin_cmd, &pin_cmd.pin1, &policy); LOG_TEST_RET(ctx, rv, "Failed to update PIN1 info"); /* 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); } rv = iasecc_chv_verify(card, &pin_cmd, policy.scbs, tries_left); /* * 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.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"); } } 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_pin_get_policy (struct sc_card *card, struct sc_pin_cmd_data *data, struct iasecc_pin_policy *pin) { 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; 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, "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) { 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) { 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) { sc_format_path("3F00", &path); path.type = SC_PATH_TYPE_FILE_ID; rv = iasecc_select_file(card, &path, NULL); LOG_TEST_GOTO_ERR(ctx, rv, "Unable to select MF"); } memset(&sdo, 0, sizeof(sdo)); sdo.sdo_class = IASECC_SDO_CLASS_CHV; sdo.sdo_ref = data->pin_reference & ~IASECC_OBJECT_REF_LOCAL; sc_log(ctx, "iasecc_pin_get_policy() reference %i", sdo.sdo_ref); rv = iasecc_sdo_get_data(card, &sdo); LOG_TEST_GOTO_ERR(ctx, rv, "Cannot get SDO PIN data"); 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); memcpy(pin->scbs, sdo.docp.scbs, sizeof(pin->scbs)); 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; } 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_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_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, int *tries_left) { 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, tries_left); 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; if (tries_left) *tries_left = data->pin1.tries_left; 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) { struct sc_context *ctx = card->ctx; struct iasecc_sdo_update update; struct iasecc_sdo sdo; unsigned scb; int rv; LOG_FUNC_CALLED(ctx); 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"); memset(&sdo, 0, sizeof(sdo)); sdo.sdo_class = IASECC_SDO_CLASS_KEYSET; sdo.sdo_ref = data->pin_reference; rv = iasecc_sdo_get_data(card, &sdo); LOG_TEST_RET(ctx, rv, "Cannot get keyset data"); 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"); update.fields[0].parent_tag = IASECC_SDO_KEYSET_TAG; update.fields[0].tag = IASECC_SDO_KEYSET_TAG_MAC; /* FIXME is it safe to modify the const value here? */ update.fields[0].value = (unsigned char *) data->pin2.data; update.fields[0].size = 16; update.fields[1].parent_tag = IASECC_SDO_KEYSET_TAG; update.fields[1].tag = IASECC_SDO_KEYSET_TAG_ENC; /* FIXME is it safe to modify the const value here? */ update.fields[1].value = (unsigned char *) data->pin2.data + 16; update.fields[1].size = 16; rv = iasecc_sm_sdo_update(card, (scb & IASECC_SCB_METHOD_MASK_REF), &update); LOG_FUNC_RETURN(ctx, rv); } /* * 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) { struct sc_context *ctx = card->ctx; 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:%zu/%zu)", data->pin_reference, data->pin_type, data->pin1.len, data->pin2.len); if (data->pin_type != SC_AC_CHV) { sc_log(ctx, "Can not change non-CHV PINs"); LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); } /* * 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; rv = iasecc_pin_merge_policy(card, &pin_cmd, &pin_cmd.pin1, &policy); LOG_TEST_RET(ctx, rv, "Failed to update PIN1 info"); if (!(pin_cmd.flags & SC_PIN_CMD_USE_PINPAD)) { rv = iasecc_chv_verify(card, &pin_cmd, policy.scbs, tries_left); LOG_TEST_RET(ctx, rv, "PIN CHV verification error"); } /* * 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 = iasecc_check_update_pin(&pin_cmd, &pin_cmd.pin2); LOG_TEST_RET(ctx, rv, "Invalid PIN2"); rv = iso_ops->pin_cmd(card, &pin_cmd, tries_left); 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) { struct sc_context *ctx = card->ctx; 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:%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"); rv = iasecc_pin_get_policy(card, data, &policy); LOG_TEST_RET(ctx, rv, "Failed to get PIN policy"); 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) { 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, tries_left); LOG_TEST_RET(ctx, rv, "iasecc_pin_reset() verify PUK error"); if (!need_all) break; } if (scb & IASECC_SCB_METHOD_SM) { rv = iasecc_sm_pin_reset(card, se_num, data); LOG_FUNC_RETURN(ctx, rv); } if (scb & IASECC_SCB_METHOD_EXT_AUTH) { rv = iasecc_sm_external_authentication(card, data->pin_reference, tries_left); LOG_TEST_RET(ctx, rv, "iasecc_pin_reset() external authentication error"); } } while(0); /* 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 = iasecc_pin_merge_policy(card, &pin_cmd, &pin_cmd.pin2, &policy); LOG_TEST_RET(ctx, rv, "Failed to update PIN2 info"); rv = iso_ops->pin_cmd(card, &pin_cmd, tries_left); LOG_FUNC_RETURN(ctx, rv); } static int iasecc_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_left) { 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:%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, tries_left); break; case SC_PIN_CMD_CHANGE: if (data->pin_type == SC_AC_AUT) rv = iasecc_keyset_change(card, data, tries_left); else rv = iasecc_pin_change(card, data, tries_left); break; case SC_PIN_CMD_UNBLOCK: rv = iasecc_pin_reset(card, data, tries_left); break; case SC_PIN_CMD_GET_INFO: rv = iasecc_pin_get_info(card, data, tries_left); break; default: sc_log(ctx, "Other pin commands not supported yet: 0x%X", data->cmd); rv = SC_ERROR_NOT_SUPPORTED; } LOG_FUNC_RETURN(ctx, rv); } static int iasecc_get_serialnr(struct sc_card *card, struct sc_serial_number *serial) { struct sc_context *ctx = card->ctx; struct sc_iin *iin = &card->serialnr.iin; struct sc_apdu apdu; unsigned char rbuf[0xC0]; size_t ii, offs, len; int rv; LOG_FUNC_CALLED(ctx); if (card->serialnr.len) goto end; memset(&card->serialnr, 0, sizeof(card->serialnr)); sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xB0, 0x80 | IASECC_SFI_EF_SN, 0); apdu.le = sizeof(rbuf); apdu.resp = rbuf; apdu.resplen = sizeof(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, "Get 'serial number' data failed"); 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; iin->country = 0; for (ii=5; ii<8; ii++) { iin->country *= 10; iin->country += (rbuf[ii/2] >> ((ii & 0x01) ? 0 : 4)) & 0x0F; } iin->issuer_id = 0; for (ii=8; ii<10; ii++) { iin->issuer_id *= 10; iin->issuer_id += (rbuf[ii/2] >> (ii & 0x01 ? 0 : 4)) & 0x0F; } /* 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 < 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 < len - offs; ii++) *(card->serialnr.value + ii) = rbuf[ii + offs + 2]; card->serialnr.len = ii; } do { char txt[0x200]; for (ii=0;iiserialnr.len;ii++) sprintf(txt + ii*2, "%02X", *(card->serialnr.value + ii)); sc_log(ctx, "serial number '%s'; mii %i; country %i; issuer_id %li", txt, iin->mii, iin->country, iin->issuer_id); } while(0); end: if (serial) memcpy(serial, &card->serialnr, sizeof(*serial)); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int iasecc_sdo_create(struct sc_card *card, struct iasecc_sdo *sdo) { struct sc_context *ctx = card->ctx; struct sc_apdu apdu; unsigned char *data = NULL, sdo_class = sdo->sdo_class; struct iasecc_sdo_update update; struct iasecc_extended_tlv *field = NULL; int rv = SC_ERROR_NOT_SUPPORTED, data_len; LOG_FUNC_CALLED(ctx); if (sdo->magic != SC_CARDCTL_IASECC_SDO_MAGIC) LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Invalid SDO data"); sc_log(ctx, "iasecc_sdo_create(card:%p) %02X%02X%02X", card, IASECC_SDO_TAG_HEADER, sdo->sdo_class | 0x80, sdo->sdo_ref); data_len = iasecc_sdo_encode_create(ctx, sdo, &data); LOG_TEST_RET(ctx, data_len, "iasecc_sdo_create() cannot encode SDO create data"); sc_log(ctx, "iasecc_sdo_create() create data(%i):%s", data_len, sc_dump_hex(data, data_len)); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xDB, 0x3F, 0xFF); apdu.data = data; apdu.datalen = data_len; apdu.lc = data_len; apdu.flags |= SC_APDU_FLAGS_CHAINING; 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, "iasecc_sdo_create() SDO put data error"); 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; if (sdo_class == IASECC_SDO_CLASS_RSA_PRIVATE) { update.fields[0] = sdo->data.prv_key.compulsory; update.fields[0].parent_tag = IASECC_SDO_PRVKEY_TAG; field = &sdo->data.prv_key.compulsory; } else if (sdo_class == IASECC_SDO_CLASS_RSA_PUBLIC) { update.fields[0] = sdo->data.pub_key.compulsory; update.fields[0].parent_tag = IASECC_SDO_PUBKEY_TAG; field = &sdo->data.pub_key.compulsory; } else if (sdo_class == IASECC_SDO_CLASS_KEYSET) { update.fields[0] = sdo->data.keyset.compulsory; update.fields[0].parent_tag = IASECC_SDO_KEYSET_TAG; field = &sdo->data.keyset.compulsory; } if (update.fields[0].value && !update.fields[0].on_card) { rv = iasecc_sdo_put_data(card, &update); LOG_TEST_RET(ctx, rv, "failed to update 'Compulsory usage' data"); if (field) field->on_card = 1; } free(data); LOG_FUNC_RETURN(ctx, rv); } /* Oberthur's specific */ static int iasecc_sdo_delete(struct sc_card *card, struct iasecc_sdo *sdo) { struct sc_context *ctx = card->ctx; struct sc_apdu apdu; unsigned char data[6] = { 0x70, 0x04, 0xBF, 0xFF, 0xFF, 0x00 }; int rv; LOG_FUNC_CALLED(ctx); if (sdo->magic != SC_CARDCTL_IASECC_SDO_MAGIC) LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Invalid SDO data"); data[2] = IASECC_SDO_TAG_HEADER; data[3] = sdo->sdo_class | 0x80; data[4] = sdo->sdo_ref; sc_log(ctx, "delete SDO %02X%02X%02X", data[2], data[3], data[4]); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xDB, 0x3F, 0xFF); apdu.data = data; apdu.datalen = sizeof(data); apdu.lc = sizeof(data); apdu.flags |= SC_APDU_FLAGS_CHAINING; 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, "delete SDO error"); LOG_FUNC_RETURN(ctx, rv); } static int iasecc_sdo_put_data(struct sc_card *card, struct iasecc_sdo_update *update) { struct sc_context *ctx = card->ctx; struct sc_apdu apdu; int ii, rv; LOG_FUNC_CALLED(ctx); if (update->magic != SC_CARDCTL_IASECC_SDO_MAGIC_PUT_DATA) LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Invalid SDO update data"); for(ii=0; update->fields[ii].tag && ii < IASECC_SDO_TAGS_UPDATE_MAX; ii++) { unsigned char *encoded = NULL; int encoded_len; encoded_len = iasecc_sdo_encode_update_field(ctx, update->sdo_class, update->sdo_ref, &update->fields[ii], &encoded); sc_log(ctx, "iasecc_sdo_put_data() encode[%i]; tag %X; encoded_len %i", ii, update->fields[ii].tag, encoded_len); LOG_TEST_RET(ctx, encoded_len, "Cannot encode update data"); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xDB, 0x3F, 0xFF); apdu.data = encoded; apdu.datalen = encoded_len; apdu.lc = encoded_len; apdu.flags |= SC_APDU_FLAGS_CHAINING; 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, "SDO put data error"); free(encoded); } LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int iasecc_sdo_key_rsa_put_data(struct sc_card *card, struct iasecc_sdo_rsa_update *update) { struct sc_context *ctx = card->ctx; unsigned char scb; int rv; LOG_FUNC_CALLED(ctx); if (update->sdo_prv_key) { sc_log(ctx, "encode private rsa in %p", &update->update_prv); rv = iasecc_sdo_encode_rsa_update(card->ctx, update->sdo_prv_key, update->p15_rsa, &update->update_prv); LOG_TEST_RET(ctx, rv, "failed to encode update of RSA private key"); } if (update->sdo_pub_key) { sc_log(ctx, "encode public rsa in %p", &update->update_pub); if (card->type == SC_CARD_TYPE_IASECC_SAGEM) { if (update->sdo_pub_key->data.pub_key.cha.value) { free(update->sdo_pub_key->data.pub_key.cha.value); memset(&update->sdo_pub_key->data.pub_key.cha, 0, sizeof(update->sdo_pub_key->data.pub_key.cha)); } } rv = iasecc_sdo_encode_rsa_update(card->ctx, update->sdo_pub_key, update->p15_rsa, &update->update_pub); LOG_TEST_RET(ctx, rv, "failed to encode update of RSA public key"); } if (update->sdo_prv_key) { sc_log(ctx, "reference of the private key to store: %X", update->sdo_prv_key->sdo_ref); if (update->sdo_prv_key->docp.acls_contact.size == 0) LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "extremely strange ... there are no ACLs"); scb = update->sdo_prv_key->docp.scbs[IASECC_ACLS_RSAKEY_PUT_DATA]; sc_log(ctx, "'UPDATE PRIVATE RSA' scb 0x%X", scb); do { unsigned all_conditions = scb & IASECC_SCB_METHOD_NEED_ALL ? 1 : 0; if ((scb & IASECC_SCB_METHOD_USER_AUTH) && !all_conditions) break; if (scb & IASECC_SCB_METHOD_EXT_AUTH) LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Not yet"); if (scb & IASECC_SCB_METHOD_SM) { #ifdef ENABLE_SM rv = iasecc_sm_rsa_update(card, scb & IASECC_SCB_METHOD_MASK_REF, update); LOG_FUNC_RETURN(ctx, rv); #else LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "built without support of Secure-Messaging"); #endif } } while(0); rv = iasecc_sdo_put_data(card, &update->update_prv); LOG_TEST_RET(ctx, rv, "failed to update of RSA private key"); } if (update->sdo_pub_key) { sc_log(ctx, "reference of the public key to store: %X", update->sdo_pub_key->sdo_ref); rv = iasecc_sdo_put_data(card, &update->update_pub); LOG_TEST_RET(ctx, rv, "failed to update of RSA public key"); } LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int iasecc_sdo_tag_from_class(unsigned sdo_class) { switch (sdo_class & ~IASECC_OBJECT_REF_LOCAL) { case IASECC_SDO_CLASS_CHV: return IASECC_SDO_CHV_TAG; case IASECC_SDO_CLASS_RSA_PRIVATE: return IASECC_SDO_PRVKEY_TAG; case IASECC_SDO_CLASS_RSA_PUBLIC: return IASECC_SDO_PUBKEY_TAG; case IASECC_SDO_CLASS_SE: return IASECC_SDO_CLASS_SE; case IASECC_SDO_CLASS_KEYSET: return IASECC_SDO_KEYSET_TAG; } return -1; } static int iasecc_sdo_get_tagged_data(struct sc_card *card, int sdo_tag, struct iasecc_sdo *sdo) { struct sc_context *ctx = card->ctx; struct sc_apdu apdu; unsigned char sbuf[0x100]; size_t offs = sizeof(sbuf) - 1; unsigned char rbuf[0x400]; int rv; 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) sbuf[offs--] = (sdo_tag >> 8) & 0xFF; sbuf[offs] = sizeof(sbuf) - offs - 1; offs--; sbuf[offs--] = sdo->sdo_ref & 0x9F; sbuf[offs--] = sdo->sdo_class | IASECC_OBJECT_REF_LOCAL; sbuf[offs--] = IASECC_SDO_TAG_HEADER; sbuf[offs] = sizeof(sbuf) - offs - 1; offs--; sbuf[offs--] = IASECC_SDO_TEMPLATE_TAG; sbuf[offs] = sizeof(sbuf) - offs - 1; offs--; sbuf[offs] = 0x4D; sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xCB, 0x3F, 0xFF); apdu.data = sbuf + offs; apdu.datalen = sizeof(sbuf) - offs; apdu.lc = sizeof(sbuf) - offs; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = 0x100; 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, "SDO get data error"); rv = iasecc_sdo_parse(card, apdu.resp, apdu.resplen, sdo); LOG_TEST_RET(ctx, rv, "cannot parse SDO data"); LOG_FUNC_RETURN(ctx, rv); } static int iasecc_sdo_get_data(struct sc_card *card, struct iasecc_sdo *sdo) { struct sc_context *ctx = card->ctx; int rv, sdo_tag; LOG_FUNC_CALLED(ctx); sdo_tag = iasecc_sdo_tag_from_class(sdo->sdo_class); rv = iasecc_sdo_get_tagged_data(card, sdo_tag, sdo); /* When there is no public data 'GET DATA' returns error */ if (rv != SC_ERROR_INCORRECT_PARAMETERS) LOG_TEST_RET(ctx, rv, "cannot parse ECC SDO data"); rv = iasecc_sdo_get_tagged_data(card, IASECC_DOCP_TAG, sdo); LOG_TEST_RET(ctx, rv, "cannot parse ECC DOCP data"); LOG_FUNC_RETURN(ctx, rv); } static int iasecc_sdo_generate(struct sc_card *card, struct iasecc_sdo *sdo) { struct sc_context *ctx = card->ctx; struct iasecc_sdo_update update_pubkey; struct sc_apdu apdu; unsigned char scb, sbuf[5], rbuf[0x400], exponent[3] = {0x01, 0x00, 0x01}; int offs = 0, rv = SC_ERROR_NOT_SUPPORTED; LOG_FUNC_CALLED(ctx); if (sdo->sdo_class != IASECC_SDO_CLASS_RSA_PRIVATE) LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "For a moment, only RSA_PRIVATE class can be accepted for the SDO generation"); 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_RSAKEY_GENERATE]; sc_log(ctx, "'generate RSA key' SCB 0x%X", scb); do { unsigned all_conditions = scb & IASECC_SCB_METHOD_NEED_ALL ? 1 : 0; if (scb & IASECC_SCB_METHOD_USER_AUTH) if (!all_conditions) break; if (scb & IASECC_SCB_METHOD_EXT_AUTH) LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Not yet"); if (scb & IASECC_SCB_METHOD_SM) { rv = iasecc_sm_rsa_generate(card, scb & IASECC_SCB_METHOD_MASK_REF, sdo); LOG_FUNC_RETURN(ctx, rv); } } while(0); memset(&update_pubkey, 0, sizeof(update_pubkey)); update_pubkey.magic = SC_CARDCTL_IASECC_SDO_MAGIC_PUT_DATA; update_pubkey.sdo_class = IASECC_SDO_CLASS_RSA_PUBLIC; update_pubkey.sdo_ref = sdo->sdo_ref; update_pubkey.fields[0].parent_tag = IASECC_SDO_PUBKEY_TAG; update_pubkey.fields[0].tag = IASECC_SDO_PUBKEY_TAG_E; update_pubkey.fields[0].value = exponent; update_pubkey.fields[0].size = sizeof(exponent); rv = iasecc_sdo_put_data(card, &update_pubkey); LOG_TEST_RET(ctx, rv, "iasecc_sdo_generate() update SDO public key failed"); offs = 0; sbuf[offs++] = IASECC_SDO_TEMPLATE_TAG; sbuf[offs++] = 0x03; sbuf[offs++] = IASECC_SDO_TAG_HEADER; sbuf[offs++] = IASECC_SDO_CLASS_RSA_PRIVATE | IASECC_OBJECT_REF_LOCAL; sbuf[offs++] = sdo->sdo_ref & ~IASECC_OBJECT_REF_LOCAL; sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x47, 0x00, 0x00); apdu.data = sbuf; apdu.datalen = offs; apdu.lc = offs; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = 0x100; 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, "SDO get data error"); LOG_FUNC_RETURN(ctx, rv); } static int iasecc_get_chv_reference_from_se(struct sc_card *card, int *se_reference) { struct sc_context *ctx = card->ctx; struct iasecc_se_info se; struct sc_crt crt; int rv; LOG_FUNC_CALLED(ctx); if (!se_reference) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Invalid arguments"); memset(&se, 0, sizeof(se)); se.reference = *se_reference; rv = iasecc_se_get_info(card, &se); LOG_TEST_RET(ctx, rv, "get SE info error"); memset(&crt, 0, sizeof(crt)); crt.tag = IASECC_CRT_TAG_AT; crt.usage = IASECC_UQB_AT_USER_PASSWORD; rv = iasecc_se_get_crt(card, &se, &crt); LOG_TEST_RET(ctx, rv, "Cannot get 'USER PASSWORD' authentication template"); sc_file_free(se.df); LOG_FUNC_RETURN(ctx, crt.refs[0]); } static int iasecc_card_ctl(struct sc_card *card, unsigned long cmd, void *ptr) { struct sc_context *ctx = card->ctx; struct iasecc_sdo *sdo = (struct iasecc_sdo *) ptr; switch (cmd) { case SC_CARDCTL_GET_SERIALNR: return iasecc_get_serialnr(card, (struct sc_serial_number *)ptr); case SC_CARDCTL_IASECC_SDO_CREATE: sc_log(ctx, "CMD SC_CARDCTL_IASECC_SDO_CREATE: sdo_class %X", sdo->sdo_class); return iasecc_sdo_create(card, (struct iasecc_sdo *) ptr); case SC_CARDCTL_IASECC_SDO_DELETE: sc_log(ctx, "CMD SC_CARDCTL_IASECC_SDO_DELETE: sdo_class %X", sdo->sdo_class); return iasecc_sdo_delete(card, (struct iasecc_sdo *) ptr); case SC_CARDCTL_IASECC_SDO_PUT_DATA: sc_log(ctx, "CMD SC_CARDCTL_IASECC_SDO_PUT_DATA: sdo_class %X", sdo->sdo_class); return iasecc_sdo_put_data(card, (struct iasecc_sdo_update *) ptr); case SC_CARDCTL_IASECC_SDO_KEY_RSA_PUT_DATA: sc_log(ctx, "CMD SC_CARDCTL_IASECC_SDO_KEY_RSA_PUT_DATA"); return iasecc_sdo_key_rsa_put_data(card, (struct iasecc_sdo_rsa_update *) ptr); case SC_CARDCTL_IASECC_SDO_GET_DATA: sc_log(ctx, "CMD SC_CARDCTL_IASECC_SDO_GET_DATA: sdo_class %X", sdo->sdo_class); return iasecc_sdo_get_data(card, (struct iasecc_sdo *) ptr); case SC_CARDCTL_IASECC_SDO_GENERATE: sc_log(ctx, "CMD SC_CARDCTL_IASECC_SDO_GET_DATA: sdo_class %X", sdo->sdo_class); return iasecc_sdo_generate(card, (struct iasecc_sdo *) ptr); case SC_CARDCTL_GET_SE_INFO: sc_log(ctx, "CMD SC_CARDCTL_GET_SE_INFO: sdo_class %X", sdo->sdo_class); return iasecc_se_get_info(card, (struct iasecc_se_info *) ptr); case SC_CARDCTL_GET_CHV_REFERENCE_IN_SE: sc_log(ctx, "CMD SC_CARDCTL_GET_CHV_REFERENCE_IN_SE"); return iasecc_get_chv_reference_from_se(card, (int *)ptr); case SC_CARDCTL_IASECC_GET_FREE_KEY_REFERENCE: sc_log(ctx, "CMD SC_CARDCTL_IASECC_GET_FREE_KEY_REFERENCE"); return iasecc_get_free_reference(card, (struct iasecc_ctl_get_free_reference *)ptr); } return SC_ERROR_NOT_SUPPORTED; } static int iasecc_decipher(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 sc_apdu apdu; unsigned char sbuf[0x200]; unsigned char resp[SC_MAX_APDU_BUFFER_SIZE]; size_t offs; int rv; LOG_FUNC_CALLED(ctx); sc_log(card->ctx, "crgram_len %"SC_FORMAT_LEN_SIZE_T"u; outlen %"SC_FORMAT_LEN_SIZE_T"u", in_len, out_len); if (!out || !out_len || in_len > SC_MAX_APDU_BUFFER_SIZE) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); offs = 0; sbuf[offs++] = 0x81; memcpy(sbuf + offs, in, in_len); offs += in_len; sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x80, 0x86); apdu.flags |= SC_APDU_FLAGS_CHAINING; apdu.data = sbuf; apdu.datalen = offs; apdu.lc = offs; apdu.resp = resp; apdu.resplen = sizeof(resp); apdu.le = 256; 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, "Card returned error"); if (out_len > apdu.resplen) out_len = apdu.resplen; memcpy(out, apdu.resp, out_len); rv = (int)out_len; LOG_FUNC_RETURN(ctx, rv); } static int iasecc_qsign_data_sha1(struct sc_context *ctx, const unsigned char *in, size_t in_len, struct iasecc_qsign_data *out) { #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); LOG_FUNC_CALLED(ctx); if (!in || !in_len || !out) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); sc_log(ctx, "sc_pkcs15_get_qsign_data() input data length %"SC_FORMAT_LEN_SIZE_T"u", in_len); memset(out, 0, sizeof(struct iasecc_qsign_data)); 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[jj*hh_size + ii] = ((*hh[jj] >> 8*(hh_size-1-ii)) & 0xFF); out->pre_hash_size = SHA_DIGEST_LENGTH; sc_log(ctx, "Pre SHA1:%s", sc_dump_hex(out->pre_hash, out->pre_hash_size)); pre_hash_Nl = md_data->Nl - (md_data->Nl % (sizeof(md_data->data) *8)); for (ii=0; iicounter[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 (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)); } 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)); 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 */ } static int iasecc_qsign_data_sha256(struct sc_context *ctx, const unsigned char *in, size_t in_len, struct iasecc_qsign_data *out) { #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); LOG_FUNC_CALLED(ctx); if (!in || !in_len || !out) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); sc_log(ctx, "sc_pkcs15_get_qsign_data() input data length %"SC_FORMAT_LEN_SIZE_T"u", in_len); memset(out, 0, sizeof(struct iasecc_qsign_data)); 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] = ((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 = md_data->Nl - (md_data->Nl % (sizeof(md_data->data) * 8)); for (ii=0; iicounter[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 (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)); } 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)); 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 */ } static int 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 sc_security_env *env = &prv->security_env; struct iasecc_qsign_data qsign_data; struct sc_apdu apdu; size_t offs = 0, hash_len = 0; unsigned char sbuf[SC_MAX_APDU_BUFFER_SIZE]; unsigned char rbuf[SC_MAX_APDU_BUFFER_SIZE]; int rv = SC_SUCCESS; LOG_FUNC_CALLED(ctx); sc_log(ctx, "iasecc_compute_signature_dst() input length %"SC_FORMAT_LEN_SIZE_T"u", in_len); if (env->operation != SC_SEC_OPERATION_SIGN) 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"); 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) { rv = iasecc_qsign_data_sha256(card->ctx, in, in_len, &qsign_data); } else LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Need RSA_HASH_SHA1 or RSA_HASH_SHA256 algorithm"); LOG_TEST_RET(ctx, rv, "Cannot get QSign data"); sc_log(ctx, "iasecc_compute_signature_dst() hash_len %"SC_FORMAT_LEN_SIZE_T"u; key_size %"SC_FORMAT_LEN_SIZE_T"u", hash_len, prv->key_size); memset(sbuf, 0, sizeof(sbuf)); sbuf[offs++] = 0x90; if (qsign_data.counter_long) { sbuf[offs++] = qsign_data.hash_size + 8; memcpy(sbuf + offs, qsign_data.pre_hash, qsign_data.pre_hash_size); offs += qsign_data.pre_hash_size; memcpy(sbuf + offs, qsign_data.counter, sizeof(qsign_data.counter)); offs += sizeof(qsign_data.counter); } else { sbuf[offs++] = 0; } sbuf[offs++] = 0x80; sbuf[offs++] = qsign_data.last_block_size; memcpy(sbuf + offs, qsign_data.last_block, qsign_data.last_block_size); offs += qsign_data.last_block_size; sc_log(ctx, "iasecc_compute_signature_dst() offs %"SC_FORMAT_LEN_SIZE_T"u; OP(meth:%X,ref:%X)", offs, prv->op_method, prv->op_ref); if (prv->op_method == SC_AC_SCB && (prv->op_ref & IASECC_SCB_METHOD_SM)) LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Not yet"); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x2A, 0x90, 0xA0); apdu.data = sbuf; apdu.datalen = offs; apdu.lc = offs; 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, "Compute signature failed"); sc_log(ctx, "iasecc_compute_signature_dst() partial hash OK"); sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x2A, 0x9E, 0x9A); apdu.resp = rbuf; apdu.resplen = prv->key_size; apdu.le = prv->key_size; 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, "Compute signature failed"); sc_log(ctx, "iasecc_compute_signature_dst() DST resplen %"SC_FORMAT_LEN_SIZE_T"u", apdu.resplen); if (apdu.resplen > out_len) LOG_TEST_RET(ctx, SC_ERROR_BUFFER_TOO_SMALL, "Result buffer too small for the DST signature"); memcpy(out, apdu.resp, apdu.resplen); LOG_FUNC_RETURN(ctx, (int)apdu.resplen); } static int 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 sc_security_env *env = &prv->security_env; struct sc_apdu apdu; size_t offs = 0, sz = 0; unsigned char rbuf[SC_MAX_APDU_BUFFER_SIZE]; int rv; LOG_FUNC_CALLED(ctx); if (env->operation != SC_SEC_OPERATION_AUTHENTICATE) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "It's not SC_SEC_OPERATION_AUTHENTICATE"); sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x88, 0x00, 0x00); apdu.datalen = in_len; apdu.data = in; apdu.lc = in_len; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = 0x100; 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, "Compute signature failed"); do { if (offs + apdu.resplen > out_len) LOG_TEST_RET(ctx, SC_ERROR_BUFFER_TOO_SMALL, "Buffer too small to return signature"); memcpy(out + offs, rbuf, apdu.resplen); offs += apdu.resplen; if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) break; if (apdu.sw1 == 0x61) { sz = apdu.sw2 == 0x00 ? 0x100 : apdu.sw2; rv = iso_ops->get_response(card, &sz, rbuf); LOG_TEST_RET(ctx, rv, "Get response error"); apdu.resplen = rv; } else { LOG_TEST_RET(ctx, SC_ERROR_INTERNAL, "Impossible error: SW1 is not 0x90 neither 0x61"); } } while(rv > 0); LOG_FUNC_RETURN(ctx, (int)offs); } static int iasecc_compute_signature(struct sc_card *card, const unsigned char *in, size_t in_len, unsigned char *out, size_t out_len) { struct sc_context *ctx; struct iasecc_private_data *prv; struct sc_security_env *env; if (!card || !in || !out) return SC_ERROR_INVALID_ARGUMENTS; ctx = card->ctx; prv = (struct iasecc_private_data *) card->drv_data; env = &prv->security_env; LOG_FUNC_CALLED(ctx); sc_log(ctx, "inlen %"SC_FORMAT_LEN_SIZE_T"u, outlen %"SC_FORMAT_LEN_SIZE_T"u", in_len, out_len); if (env->operation == SC_SEC_OPERATION_SIGN) return iasecc_compute_signature_dst(card, in, in_len, out, out_len); else if (env->operation == SC_SEC_OPERATION_AUTHENTICATE) return iasecc_compute_signature_at(card, in, in_len, out, out_len); LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); } static int iasecc_read_public_key(struct sc_card *card, unsigned type, struct sc_path *key_path, unsigned ref, unsigned size, unsigned char **out, size_t *out_len) { struct sc_context *ctx = card->ctx; struct iasecc_sdo sdo; struct sc_pkcs15_bignum bn[2]; struct sc_pkcs15_pubkey_rsa rsa_key; int rv; LOG_FUNC_CALLED(ctx); if (type != SC_ALGORITHM_RSA) LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); 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_GOTO_ERR(ctx, rv, "failed to read public key: cannot get RSA SDO data"); if (out) *out = NULL; if (out_len) *out_len = 0; bn[0].data = (unsigned char *) malloc(sdo.data.pub_key.n.size); if (!bn[0].data) 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_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); rsa_key.modulus = bn[0]; rsa_key.exponent = bn[1]; rv = sc_pkcs15_encode_pubkey_rsa(ctx, &rsa_key, out, out_len); 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) free(bn[1].data); iasecc_sdo_free_fields(card, &sdo); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int iasecc_get_free_reference(struct sc_card *card, struct iasecc_ctl_get_free_reference *ctl_data) { struct sc_context *ctx = card->ctx; struct iasecc_sdo *sdo = NULL; int idx, rv; LOG_FUNC_CALLED(ctx); if ((ctl_data->key_size % 0x40) || ctl_data->index < 1 || (ctl_data->index > IASECC_OBJECT_REF_MAX)) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); sc_log(ctx, "get reference for key(index:%i,usage:%X,access:%X)", ctl_data->index, ctl_data->usage, ctl_data->access); /* TODO: when looking for the slot for the signature keys, check also PSO_SIGNATURE ACL */ for (idx = ctl_data->index; idx <= IASECC_OBJECT_REF_MAX; idx++) { unsigned char sdo_tag[3] = { IASECC_SDO_TAG_HEADER, IASECC_OBJECT_REF_LOCAL | IASECC_SDO_CLASS_RSA_PRIVATE, idx }; size_t sz; if (sdo) iasecc_sdo_free(card, sdo); rv = iasecc_sdo_allocate_and_parse(card, sdo_tag, 3, &sdo); LOG_TEST_RET(ctx, rv, "cannot parse SDO data"); rv = iasecc_sdo_get_data(card, sdo); if (rv == SC_ERROR_DATA_OBJECT_NOT_FOUND) { iasecc_sdo_free(card, sdo); 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); } 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", idx, sz, ctl_data->key_size); if (sz != ctl_data->key_size / 8) { sc_log(ctx, "key index %i ignored: different key sizes %"SC_FORMAT_LEN_SIZE_T"u/%"SC_FORMAT_LEN_SIZE_T"u", idx, sz, ctl_data->key_size / 8); continue; } if (sdo->docp.non_repudiation.value) { sc_log(ctx, "non repudiation flag %X", sdo->docp.non_repudiation.value[0]); if ((ctl_data->usage & SC_PKCS15_PRKEY_USAGE_NONREPUDIATION) && !(*sdo->docp.non_repudiation.value)) { sc_log(ctx, "key index %i ignored: need non repudiation", idx); continue; } if (!(ctl_data->usage & SC_PKCS15_PRKEY_USAGE_NONREPUDIATION) && *sdo->docp.non_repudiation.value) { sc_log(ctx, "key index %i ignored: don't need non-repudiation", idx); continue; } } if (ctl_data->access & SC_PKCS15_PRKEY_ACCESS_LOCAL) { if (sdo->docp.scbs[IASECC_ACLS_RSAKEY_GENERATE] == IASECC_SCB_NEVER) { sc_log(ctx, "key index %i ignored: GENERATE KEY not allowed", idx); continue; } } else { if (sdo->docp.scbs[IASECC_ACLS_RSAKEY_PUT_DATA] == IASECC_SCB_NEVER) { sc_log(ctx, "key index %i ignored: PUT DATA not allowed", idx); continue; } } if ((ctl_data->usage & SC_PKCS15_PRKEY_USAGE_NONREPUDIATION) && (ctl_data->usage & SC_PKCS15_PRKEY_USAGE_SIGN)) { if (sdo->docp.scbs[IASECC_ACLS_RSAKEY_PSO_SIGN] == IASECC_SCB_NEVER) { sc_log(ctx, "key index %i ignored: PSO SIGN not allowed", idx); continue; } } else if (ctl_data->usage & SC_PKCS15_PRKEY_USAGE_SIGN) { if (sdo->docp.scbs[IASECC_ACLS_RSAKEY_INTERNAL_AUTH] == IASECC_SCB_NEVER) { sc_log(ctx, "key index %i ignored: INTERNAL AUTHENTICATE not allowed", idx); continue; } } if (ctl_data->usage & (SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP)) { if (sdo->docp.scbs[IASECC_ACLS_RSAKEY_PSO_DECIPHER] == IASECC_SCB_NEVER) { sc_log(ctx, "key index %i ignored: PSO DECIPHER not allowed", idx); continue; } } break; } ctl_data->index = idx; if (idx > IASECC_OBJECT_REF_MAX) LOG_FUNC_RETURN(ctx, SC_ERROR_DATA_OBJECT_NOT_FOUND); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static struct sc_card_driver * sc_get_driver(void) { struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); if (!iso_ops) iso_ops = iso_drv->ops; iasecc_ops = *iso_ops; iasecc_ops.match_card = iasecc_match_card; iasecc_ops.init = iasecc_init; iasecc_ops.finish = iasecc_finish; iasecc_ops.read_binary = iasecc_read_binary; /* write_binary: ISO7816 implementation works */ /* update_binary: ISO7816 implementation works */ iasecc_ops.erase_binary = iasecc_erase_binary; /* resize_binary */ /* read_record: Untested */ /* write_record: Untested */ /* append_record: Untested */ /* update_record: Untested */ iasecc_ops.select_file = iasecc_select_file; /* get_response: Untested */ iasecc_ops.get_challenge = iasecc_get_challenge; iasecc_ops.logout = iasecc_logout; /* restore_security_env */ iasecc_ops.set_security_env = iasecc_set_security_env; iasecc_ops.decipher = iasecc_decipher; iasecc_ops.compute_signature = iasecc_compute_signature; iasecc_ops.create_file = iasecc_create_file; iasecc_ops.delete_file = iasecc_delete_file; /* list_files */ iasecc_ops.check_sw = iasecc_check_sw; iasecc_ops.card_ctl = iasecc_card_ctl; iasecc_ops.process_fci = iasecc_process_fci; /* construct_fci: Not needed */ iasecc_ops.pin_cmd = iasecc_pin_cmd; /* get_data: Not implemented */ /* put_data: Not implemented */ /* delete_record: Not implemented */ iasecc_ops.read_public_key = iasecc_read_public_key; return &iasecc_drv; } struct sc_card_driver * sc_get_iasecc_driver(void) { return sc_get_driver(); } #else /* we need to define the functions below to export them */ #include "errors.h" int iasecc_se_get_info() { return SC_ERROR_NOT_SUPPORTED; } #endif /* ENABLE_OPENSSL */ OpenSC-0.26.1/src/libopensc/card-idprime.c000066400000000000000000001143451474147347300202260ustar00rootroot00000000000000/* * 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 */ 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:00:00:00:00:ff:ff:ff:ff:ff:ff:00", "based Gemalto IDPrime 930 (eToken 5110+ FIPS)", SC_CARD_TYPE_IDPRIME_930, 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: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:ff:ff: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 } }; 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_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: 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); if (card->type == SC_CARD_TYPE_IDPRIME_930 || card->type == SC_CARD_TYPE_IDPRIME_940) { _sc_card_add_rsa_alg(card, 4096, flags, 0); } if (card->type == SC_CARD_TYPE_IDPRIME_930 || card->type == SC_CARD_TYPE_IDPRIME_940 || card->type == SC_CARD_TYPE_IDPRIME_840) { /* Set up algorithm info for EC */ flags = SC_ALGORITHM_ECDSA_RAW | SC_ALGORITHM_ECDSA_HASH_NONE; 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); } 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_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; if (apdu.le > sc_get_max_recv_size(card)) { /* The lower layers will automatically do a GET RESPONSE, if possible. * All other workarounds must be carried out by the upper layers. */ apdu.le = sc_get_max_recv_size(card); } 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; if (card == NULL || crgram == NULL || out == NULL) { return SC_ERROR_INVALID_ARGUMENTS; } LOG_FUNC_CALLED(card->ctx); 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] = 0x81; /* padding indicator byte, 0x81 = Proprietary */ memcpy(sbuf + 1, crgram, crgram_len); apdu.data = sbuf; apdu.lc = crgram_len + 1; if (apdu.lc > sc_get_max_send_size(card)) { /* The lower layers will automatically do chaining */ apdu.flags |= SC_APDU_FLAGS_CHAINING; } if (apdu.le > sc_get_max_recv_size(card)) { /* The lower layers will automatically do a GET RESPONSE, if possible. * All other workarounds must be carried out by the upper layers. */ apdu.le = sc_get_max_recv_size(card); } 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(); } OpenSC-0.26.1/src/libopensc/card-isoApplet.c000066400000000000000000001256571474147347300205450ustar00rootroot00000000000000/* * Support for the IsoApplet JavaCard Applet. * * Copyright (C) 2014 Philip Wendland * * 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 "asn1.h" #include "cardctl.h" #include "internal.h" #include "log.h" #include "opensc.h" #include "pkcs15.h" #include "types.h" #define ISOAPPLET_ALG_REF_ECDSA 0x21 #define ISOAPPLET_ALG_REF_RSA_PAD_PKCS1 0x11 #define ISOAPPLET_ALG_REF_RSA_PAD_PSS 0x12 #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 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 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 long sec_env_ec_field_length; unsigned int isoapplet_version; unsigned int isoapplet_features; }; #define DRVDATA(card) ((struct isoApplet_drv_data *) ((card)->drv_data)) /* Operations supported by the applet. */ static struct sc_card_operations isoApplet_ops; /* A reference to the iso7816_* functions. * Initialized in sc_get_driver. */ static const struct sc_card_operations *iso_ops = NULL; /* The description of the driver. */ static struct sc_card_driver isoApplet_drv = { "Javacard with IsoApplet", "isoApplet", &isoApplet_ops, NULL, 0, NULL }; static struct isoapplet_supported_ec_curves { struct sc_object_id oid; size_t size; unsigned int min_applet_version; } ec_curves[] = { {{{1, 2, 840, 10045, 3, 1, 1, -1}}, 192, 0x0000}, /* secp192r1, nistp192, prime192v1, ansiX9p192r1 */ {{{1, 3, 132, 0, 33, -1}}, 224, 0x0000}, /* secp224r1, nistp224 */ {{{1, 2, 840, 10045, 3, 1, 7, -1}}, 256, 0x0000}, /* secp256r1, nistp256, prime256v1, ansiX9p256r1 */ {{{1, 3, 132, 0, 34, -1}}, 384, 0x0000}, /* secp384r1, nistp384, prime384v1, ansiX9p384r1 */ {{{1, 3, 36, 3, 3, 2, 8, 1, 1, 3, -1}}, 192, 0x0000}, /* brainpoolP192r1 */ {{{1, 3, 36, 3, 3, 2, 8, 1, 1, 5, -1}}, 224, 0x0000}, /* brainpoolP224r1 */ {{{1, 3, 36, 3, 3, 2, 8, 1, 1, 7, -1}}, 256, 0x0000}, /* brainpoolP256r1 */ {{{1, 3, 36, 3, 3, 2, 8, 1, 1, 9, -1}}, 320, 0x0000}, /* brainpoolP320r1 */ {{{1, 3, 132, 0, 31, -1}}, 192, 0x0006}, /* secp192k1 */ {{{1, 3, 132, 0, 10, -1}}, 256, 0x0006}, /* secp256k1 */ {{{-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 length of aid. * * @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) { 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; rv = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(ctx, rv, "APDU transmit failure."); rv = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(card->ctx, rv, "Card returned error"); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } static int isoApplet_finish(sc_card_t *card) { struct isoApplet_drv_data *drvdata=DRVDATA(card); LOG_FUNC_CALLED(card->ctx); if (drvdata) { free(drvdata); card->drv_data=NULL; } LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } static int isoApplet_match_card(sc_card_t *card) { int rv; rv = isoApplet_select_applet(card, isoApplet_aid, sizeof(isoApplet_aid)); if(rv != SC_SUCCESS) { return 0; } 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. */ sc_apdu_t apdu; sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xa4, 0x04, 0x00); apdu.lc = sizeof(isoApplet_aid); apdu.data = isoApplet_aid; apdu.datalen = sizeof(isoApplet_aid); apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = 256; rv = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(ctx, rv, "APDU transmit failure."); rv = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(ctx, rv, "Error selecting applet."); rv = (int) apdu.resplen; } if (rv < 0) { LOG_TEST_RET(ctx, rv, "Card returned error."); } /* Fill up drvdata */ if(rv >= 3) { drvdata->isoapplet_version = rbuf[0] << 8 | rbuf[1]; drvdata->isoapplet_features = rbuf[2]; } return SC_SUCCESS; } static int isoApplet_init(sc_card_t *card) { int i, r; unsigned int major_version = 0; unsigned long flags = 0; unsigned long ext_flags = 0; struct isoApplet_drv_data *drvdata; LOG_FUNC_CALLED(card->ctx); drvdata=calloc(1, sizeof(*drvdata)); if (!drvdata) LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); card->drv_data = drvdata; card->cla = 0x00; /* Obtain applet version and specific features */ 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) { 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); } if(drvdata->isoapplet_features & ISOAPPLET_API_FEATURE_EXT_APDU) card->caps |= SC_CARD_CAP_APDU_EXT; if(drvdata->isoapplet_features & ISOAPPLET_API_FEATURE_SECURE_RANDOM) card->caps |= SC_CARD_CAP_RNG; 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. * * Curves supported by the pkcs15-init driver are indicated per curve. This * should be kept in sync with the explicit parameters in the pkcs15-init * driver. */ flags = 0; 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; ext_flags |= SC_ALGORITHM_EXT_EC_F_P; for (i=0; ec_curves[i].oid.value[0] >= 0; i++) { if(drvdata->isoapplet_version >= ec_curves[i].min_applet_version) _sc_card_add_ec_alg(card, ec_curves[i].size, flags, ext_flags, &ec_curves[i].oid); } } /* RSA */ flags = 0; flags |= SC_ALGORITHM_RSA_PAD_PKCS1; 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); } /* * @brief convert an OpenSC ACL entry to the security condition * byte used by the IsoApplet. * * Used by IsoApplet_create_file to parse OpenSC ACL entries * into ISO 7816-4 Table 20 security condition bytes. * * @param entry The OpenSC ACL entry. * * @return The security condition byte. No restriction (0x00) * if unknown operation. */ static u8 isoApplet_acl_to_security_condition_byte(const sc_acl_entry_t *entry) { if(!entry) return 0x00; switch(entry->method) { case SC_AC_CHV: return 0x90; case SC_AC_NEVER: return 0xFF; case SC_AC_NONE: default: return 0x00; } } /* * The reason for this function is that OpenSC doesn't set any * Security Attribute Tag in the FCI upon file creation if there * is no file->sec_attr. I set the file->sec_attr to a format * understood by the applet (ISO 7816-4 tables 16, 17 and 20). * The iso7816_create_file will then set this as Tag 86 - Sec. * Attr. Prop. Format. * The applet will then be able to set and enforce access rights * for any file created by OpenSC. Without this function, the * applet would not know where to enforce security rules and * when. * * Note: IsoApplet currently only supports a "onepin" option. * * Format of the sec_attr: 8 Bytes: * 7 - ISO 7816-4 table 16 or 17 * 6 to 0 - ISO 7816-4 table 20 */ static int isoApplet_create_file(sc_card_t *card, sc_file_t *file) { int r = 0; LOG_FUNC_CALLED(card->ctx); if(file->sec_attr_len == 0) { u8 access_buf[8]; int idx[8], i; if(file->type == SC_FILE_TYPE_DF) { const int df_idx[8] = /* These are the SC operations. */ { 0, /* Reserved. */ SC_AC_OP_DELETE_SELF, /* b6 */ SC_AC_OP_LOCK, /* b5 */ SC_AC_OP_ACTIVATE, /* b4 */ SC_AC_OP_DEACTIVATE, /* b3 */ SC_AC_OP_CREATE_DF, /* b2 */ SC_AC_OP_CREATE_EF, /* b1 */ SC_AC_OP_DELETE /* b0 */ }; for(i=0; i<8; i++) { idx[i] = df_idx[i]; } } else /* EF */ { const int ef_idx[8] = { 0, /* Reserved. */ SC_AC_OP_DELETE_SELF, /* b6 */ SC_AC_OP_LOCK, /* b5 */ SC_AC_OP_ACTIVATE, /* b4 */ SC_AC_OP_DEACTIVATE, /* b3 */ SC_AC_OP_WRITE, /* b2 */ SC_AC_OP_UPDATE, /* b1 */ SC_AC_OP_READ /* b0 */ }; for(i=0; i<8; i++) { idx[i] = ef_idx[i]; } } /* Now idx contains the operation identifiers. * We now search for the OPs. */ access_buf[0] = 0xFF; /* A security condition byte is present for every OP. (Table 19) */ for(i=1; i<8; i++) { const sc_acl_entry_t *entry; entry = sc_file_get_acl_entry(file, idx[i]); access_buf[i] = isoApplet_acl_to_security_condition_byte(entry); } r = sc_file_set_sec_attr(file, access_buf, 8); LOG_TEST_RET(card->ctx, r, "Error adding security attribute."); } r = iso_ops->create_file(card, file); LOG_FUNC_RETURN(card->ctx, r); } /* * Add an ACL entry to the OpenSC file struct, according to the operation * and the saByte (Encoded according to IsoApplet FCI proprietary security * information, see also ISO 7816-4 table 20). * * @param[in,out] file * @param[in] operation The OpenSC operation. * @param[in] saByte The security condition byte returned by the applet. */ static int isoApplet_add_sa_to_acl(sc_file_t *file, unsigned int operation, u8 saByte) { int r; switch(saByte) { case 0x90: r = sc_file_add_acl_entry(file, operation, SC_AC_CHV, 1); if(r < 0) return r; break; case 0xFF: r = sc_file_add_acl_entry(file, operation, SC_AC_NEVER, SC_AC_KEY_REF_NONE); if(r < 0) return r; break; case 0x00: r = sc_file_add_acl_entry(file, operation, SC_AC_NONE, SC_AC_KEY_REF_NONE); if(r < 0) return r; break; default: r = sc_file_add_acl_entry(file, operation, SC_AC_UNKNOWN, SC_AC_KEY_REF_NONE); if(r < 0) return r; } return SC_SUCCESS; } /* * This function first calls the iso7816.c process_fci() for any other FCI * information and then updates the ACL of the OpenSC file struct according * to the FCI from the applet. */ static int isoApplet_process_fci(sc_card_t *card, sc_file_t *file, const u8 *buf, size_t buflen) { int r; u8 *sa = NULL; LOG_FUNC_CALLED(card->ctx); r = iso_ops->process_fci(card, file, buf, buflen); LOG_TEST_RET(card->ctx, r, "Error while processing the FCI."); /* Construct the ACL from the sec_attr. */ if(file->sec_attr && file->sec_attr_len == 8) { sa = file->sec_attr; if(sa[0] != 0xFF) { LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "File security attribute does not contain a ACL byte for every operation."); } if(file->type == SC_FILE_TYPE_DF) { r = isoApplet_add_sa_to_acl(file, SC_AC_OP_DELETE_SELF, sa[1]); LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); r = isoApplet_add_sa_to_acl(file, SC_AC_OP_LOCK, sa[2]); LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); r = isoApplet_add_sa_to_acl(file, SC_AC_OP_ACTIVATE, sa[3]); LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); r = isoApplet_add_sa_to_acl(file, SC_AC_OP_DEACTIVATE, sa[4]); LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); r = isoApplet_add_sa_to_acl(file, SC_AC_OP_CREATE_DF, sa[5]); LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); r = isoApplet_add_sa_to_acl(file, SC_AC_OP_CREATE_EF, sa[6]); LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); r = isoApplet_add_sa_to_acl(file, SC_AC_OP_DELETE, sa[7]); LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); } else if(file->type == SC_FILE_TYPE_INTERNAL_EF || file->type == SC_FILE_TYPE_WORKING_EF) { r = isoApplet_add_sa_to_acl(file, SC_AC_OP_DELETE_SELF, sa[1]); LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); r = isoApplet_add_sa_to_acl(file, SC_AC_OP_LOCK, sa[2]); LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); r = isoApplet_add_sa_to_acl(file, SC_AC_OP_ACTIVATE, sa[3]); LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); r = isoApplet_add_sa_to_acl(file, SC_AC_OP_DEACTIVATE, sa[4]); LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); r = isoApplet_add_sa_to_acl(file, SC_AC_OP_WRITE, sa[5]); LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); r = isoApplet_add_sa_to_acl(file, SC_AC_OP_UPDATE, sa[6]); LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); r = isoApplet_add_sa_to_acl(file, SC_AC_OP_READ, sa[7]); LOG_TEST_RET(card->ctx, r, "Error adding ACL entry."); } } LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } /* * @brief Encode the EC parameters as a concatenation of TLV entries. * * The format is: * 81 - prime * 82 - coefficient A * 83 - coefficient B * 84 - base point G * 85 - order * 87 - cofactor * * @param[in] card * @param[in] params The ECparameters containing the information of the curve. * @param[out] out The array the encoded parameters are written to. * @param[in] out_len The size of out * @param[out] ptr A pointer pointing to the end of the parameters in out * (the first untouched byte behind the parameters). */ static int isoApplet_put_ec_params(sc_card_t *card, sc_cardctl_isoApplet_ec_parameters_t *params, u8 *out, size_t out_len, u8 **ptr) { u8 *p = out; int r; LOG_FUNC_CALLED(card->ctx); if(!params || !params->prime.value || !params->coefficientA.value || !params->coefficientB.value || !params->basePointG.value || !params->order.value || !params->coFactor.value) { LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "Error: EC params not present."); } if(out == NULL || out_len == 0) { LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "Error: Parameter out is NULL or outlen is zero."); } r = sc_asn1_put_tag(0x81, params->prime.value, params->prime.len, p, out_len - (p - out), &p); LOG_TEST_RET(card->ctx, r, "Error in handling TLV."); r = sc_asn1_put_tag(0x82, params->coefficientA.value, params->coefficientA.len, p, out_len - (p - out), &p); LOG_TEST_RET(card->ctx, r, "Error in handling TLV."); r = sc_asn1_put_tag(0x83, params->coefficientB.value, params->coefficientB.len, p, out_len - (p - out), &p); LOG_TEST_RET(card->ctx, r, "Error in handling TLV."); r = sc_asn1_put_tag(0x84, params->basePointG.value, params->basePointG.len, p, out_len - (p - out), &p); LOG_TEST_RET(card->ctx, r, "Error in handling TLV."); r = sc_asn1_put_tag(0x85, params->order.value, params->order.len, p, out_len - (p - out), &p); LOG_TEST_RET(card->ctx, r, "Error in handling TLV."); r = sc_asn1_put_tag(0x87, params->coFactor.value, params->coFactor.len, p, out_len - (p - out), &p); LOG_TEST_RET(card->ctx, r, "Error in handling TLV."); if (ptr != NULL) *ptr = p; LOG_FUNC_RETURN(card->ctx, r); } /* * @brief Generate a private key on the card. */ static int 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_RESP_SIZE]; u8 sbuf[SC_MAX_EXT_APDU_DATA_SIZE]; u8 *p; const u8 *inner_tag_value; const u8 *outer_tag_value; unsigned int tag; size_t outer_tag_len; size_t inner_tag_len; unsigned int cla; size_t sz; LOG_FUNC_CALLED(card->ctx); /* MANAGE SECURITY ENVIRONMENT (SET). Set the algorithm and key references. */ sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0x00); p = sbuf; *p++ = 0x80; /* algorithm reference */ *p++ = 0x01; *p++ = args->algorithm_ref; *p++ = 0x84; /* Private key reference */ *p++ = 0x01; *p++ = args->priv_key_ref; sz = p - sbuf; p = NULL; apdu.lc = sz; apdu.datalen = sz; apdu.data = sbuf; 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"); /* GENERATE ASYMMETRIC KEY PAIR * We use a larger buffer here, even if the card does not support extended apdus. * 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 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) { sc_format_apdu(card, &apdu, SC_APDU_CASE_4, 0x46, 0x00, 0x00); apdu.data = sbuf; p = sbuf; r = isoApplet_put_ec_params(card, &args->pubkey.ec.params, p, sizeof(sbuf), &p); LOG_TEST_RET(card->ctx, r, "Error composing EC params."); apdu.datalen = p - sbuf; apdu.lc = p - sbuf; /* Use APDU chaining if the card does not support extended apdus * and the data does not fit in one short apdu. */ if ((apdu.datalen > 255) && !(card->caps & SC_CARD_CAP_APDU_EXT)) { apdu.flags |= SC_APDU_FLAGS_CHAINING; } } else { sc_format_apdu(card, &apdu, SC_APDU_CASE_2, 0x46, 0x42, 0x00); } apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); 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"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); if(apdu.sw1 == 0x6A && apdu.sw2 == 0x81) { sc_log(card->ctx, "Key generation not supported by the card with that particular key type. " "Your card may not support the specified algorithm used by the applet / specified by you. " "In most cases, this happens when trying to generate EC keys not supported by your java card. " "In this case, look for supported field lengths and whether FP and/or F2M are supported."); } LOG_TEST_RET(card->ctx, r, "Card returned error"); /* Parse the public key / response. */ outer_tag_value = apdu.resp; r = sc_asn1_read_tag(&outer_tag_value, apdu.resplen, &cla, &tag, &outer_tag_len); LOG_TEST_RET(card->ctx, r, "Error in ASN1 handling."); /* Interindustry template for nesting one set of public key data objects */ if((tag != 0x1F49) || (cla != 0x60)) { LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "The data returned by the card is unexpected."); } switch(args->algorithm_ref) { 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); 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."); } if(inner_tag_len > args->pubkey.rsa.modulus.len) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_BUFFER_TOO_SMALL); } memcpy(args->pubkey.rsa.modulus.value, inner_tag_value, inner_tag_len); args->pubkey.rsa.modulus.len = inner_tag_len; /* Exponent tag (82) */ inner_tag_value = sc_asn1_find_tag(card->ctx, outer_tag_value, outer_tag_len, (unsigned int) 0x82, &inner_tag_len); if(inner_tag_value == NULL || inner_tag_len != 3) { LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "Card returned no or a invalid exponent."); } if(inner_tag_len > args->pubkey.rsa.exponent.len) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_BUFFER_TOO_SMALL); } if(memcmp(inner_tag_value, "\x01\x00\x01", 3) != 0) { LOG_TEST_RET(card->ctx, SC_ERROR_INCOMPATIBLE_KEY, "Key generation error: Unexpected public key exponent."); } memcpy(args->pubkey.rsa.exponent.value, inner_tag_value, inner_tag_len); args->pubkey.rsa.exponent.len = inner_tag_len; p = NULL; break; case SC_ISOAPPLET_ALG_REF_EC_GEN: /* Compare the parameters received from the card to the ones sent to the card. */ 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 != args->pubkey.ec.params.prime.len || memcmp(inner_tag_value, args->pubkey.ec.params.prime.value, inner_tag_len) != 0) LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "Card returned no or a invalid prime."); inner_tag_value = sc_asn1_find_tag(card->ctx, outer_tag_value, outer_tag_len, (unsigned int) 0x82, &inner_tag_len); if(inner_tag_value == NULL || inner_tag_len != args->pubkey.ec.params.coefficientA.len || memcmp(inner_tag_value, args->pubkey.ec.params.coefficientA.value, inner_tag_len) != 0) LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "Card returned no or a invalid coefficient A."); inner_tag_value = sc_asn1_find_tag(card->ctx, outer_tag_value, outer_tag_len, (unsigned int) 0x83, &inner_tag_len); if(inner_tag_value == NULL || inner_tag_len != args->pubkey.ec.params.coefficientB.len || memcmp(inner_tag_value, args->pubkey.ec.params.coefficientB.value, inner_tag_len) != 0) LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "Card returned no or a invalid coefficient B."); inner_tag_value = sc_asn1_find_tag(card->ctx, outer_tag_value, outer_tag_len, (unsigned int) 0x84, &inner_tag_len); if(inner_tag_value == NULL || inner_tag_len != args->pubkey.ec.params.basePointG.len || memcmp(inner_tag_value, args->pubkey.ec.params.basePointG.value, inner_tag_len) != 0) LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "Card returned no or a invalid base point G."); inner_tag_value = sc_asn1_find_tag(card->ctx, outer_tag_value, outer_tag_len, (unsigned int) 0x85, &inner_tag_len); if(inner_tag_value == NULL || inner_tag_len != args->pubkey.ec.params.order.len || memcmp(inner_tag_value, args->pubkey.ec.params.order.value, inner_tag_len) != 0) LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "Card returned no or a invalid order."); inner_tag_value = sc_asn1_find_tag(card->ctx, outer_tag_value, outer_tag_len, (unsigned int) 0x87, &inner_tag_len); if(inner_tag_value == NULL || inner_tag_len != args->pubkey.ec.params.coFactor.len || memcmp(inner_tag_value, args->pubkey.ec.params.coFactor.value, inner_tag_len) != 0) LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "Card returned no or a invalid cofactor."); /* Extract public key */ inner_tag_value = sc_asn1_find_tag(card->ctx, outer_tag_value, outer_tag_len, (unsigned int) 0x86, &inner_tag_len); if(inner_tag_value == NULL || inner_tag_len != args->pubkey.ec.ecPointQ.len) LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "Card returned no or a invalid EC point Q."); memcpy(args->pubkey.ec.ecPointQ.value, inner_tag_value, inner_tag_len); break; default: LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED, "Unable to parse public key: Unsupported algorithm."); }/* switch */ LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } /* * @brief Use PUT DATA to import a private RSA key. * * For simplicity, command chaining has to be used. One chunk (apdu) must contain * one RSA field (P, Q, etc.). The first apdu must contain the outer tag (7F48). * * @param card * @param rsa The RSA private key to import. * * @return SC_ERROR_INVALID_ARGUMENTS: The RSA key does not contain CRT fields. * other errors: Transmit errors / errors returned by card. */ 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_DATA_SIZE]; u8 *p = NULL; int r; size_t tags_len; LOG_FUNC_CALLED(card->ctx); if(!args->privkey.rsa.p.value || !args->privkey.rsa.q.value || !args->privkey.rsa.iqmp.value || !args->privkey.rsa.dmp1.value || !args->privkey.rsa.dmq1.value) { LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "RSA key is missing information."); } /* Note: The format is according to ISO 2-byte tag 7F48 * "T-L pair to indicate a private key data object" */ /* Calculate the length of all inner tag-length-value entries, but do not write anything yet. */ tags_len = 0; r = sc_asn1_put_tag(0x92, NULL, args->privkey.rsa.p.len, NULL, 0, NULL); LOG_TEST_RET(card->ctx, r, "Error handling TLV."); tags_len += r; r = sc_asn1_put_tag(0x93, NULL, args->privkey.rsa.q.len, NULL, 0, NULL); LOG_TEST_RET(card->ctx, r, "Error handling TLV."); tags_len += r; r = sc_asn1_put_tag(0x94, NULL, args->privkey.rsa.iqmp.len, NULL, 0, NULL); LOG_TEST_RET(card->ctx, r, "Error handling TLV."); tags_len += r; r = sc_asn1_put_tag(0x95, NULL, args->privkey.rsa.dmp1.len, NULL, 0, NULL); LOG_TEST_RET(card->ctx, r, "Error handling TLV."); tags_len += r; r = sc_asn1_put_tag(0x96, NULL, args->privkey.rsa.dmq1.len, NULL, 0, NULL); LOG_TEST_RET(card->ctx, r, "Error handling TLV."); tags_len += r; /* Write the outer tag and length information. */ p = sbuf; r = sc_asn1_put_tag(0x7F48, NULL, tags_len, p, sizeof(sbuf), &p); LOG_TEST_RET(card->ctx, r, "Error handling TLV."); /* Write inner tags. */ /* p */ r = sc_asn1_put_tag(0x92, args->privkey.rsa.p.value, args->privkey.rsa.p.len, p, sizeof(sbuf) - (p - sbuf), &p); if(r < 0) goto out; /* q */ r = sc_asn1_put_tag(0x93, args->privkey.rsa.q.value, args->privkey.rsa.q.len, p, sizeof(sbuf) - (p - sbuf), &p); if(r < 0) goto out; /* 1/q mod p */ r = sc_asn1_put_tag(0x94, args->privkey.rsa.iqmp.value, args->privkey.rsa.iqmp.len, p, sizeof(sbuf) - (p - sbuf), &p); if(r < 0) goto out; /* d mod (p-1) */ r = sc_asn1_put_tag(0x95, args->privkey.rsa.dmp1.value, args->privkey.rsa.dmp1.len, p, sizeof(sbuf) - (p - sbuf), &p); if(r < 0) goto out; /* d mod (q-1) */ r = sc_asn1_put_tag(0x96, args->privkey.rsa.dmq1.value, args->privkey.rsa.dmq1.len, p, sizeof(sbuf) - (p - sbuf), &p); if(r < 0) goto out; /* Send to card, using chaining or extended APDUs. */ sc_format_apdu(card, &apdu, SC_APDU_CASE_3, 0xDB, 0x3F, 0xFF); apdu.data = sbuf; apdu.datalen = p - sbuf; apdu.lc = p - sbuf; if ((card->caps & SC_CARD_CAP_APDU_EXT) == 0) { /* The lower layers will automatically do chaining */ apdu.flags |= SC_APDU_FLAGS_CHAINING; } r = sc_transmit_apdu(card, &apdu); if(r < 0) goto out; r = sc_check_sw(card, apdu.sw1, apdu.sw2); if(apdu.sw1 == 0x6A && apdu.sw2 == 0x81) { sc_log(card->ctx, "Key import not supported by the card with that particular key type. " "Your card may not support the specified algorithm used by the applet / specified by you. " "In most cases, this happens when trying to import EC keys not supported by your java card. " "In this case, look for supported field lengths and whether FP and/or F2M are supported. " "If you tried to import a private RSA key, check the key length."); } if(apdu.sw1 == 0x69 && apdu.sw2 == 0x00) { sc_log(card->ctx, "Key import not allowed by the applet's security policy. " "If you want to allow key import, set DEF_PRIVATE_KEY_IMPORT_ALLOWED in the IsoApplet," " rebuild and reinstall the applet."); } if(r < 0) goto out; r = SC_SUCCESS; out: sc_mem_clear(sbuf, sizeof(sbuf)); LOG_FUNC_RETURN(card->ctx, r); } /* * @brief Use PUT DATA to import a private EC key. * * Format of transmitted data: * 0xE0 - Private class, constructed encoding, number one. * 0x81 - prime * 0x82 - coefficient A * 0x83 - coefficient B * 0x84 - base point G * 0x85 - order * 0x87 - cofactor * 0x88 - private D (private key) * * @param card * @param ec The EC private key to import. * * @return SC_ERROR_INVALID_ARGUMENTS: Curve parameters or private component is missing. * other errors: Transmit errors / errors returned by card. * ASN1 errors. */ 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_DATA_SIZE]; int r; u8 *p; size_t tags_len; LOG_FUNC_CALLED(card->ctx); if(!args->privkey.ec.privateD.value || !args->privkey.ec.params.prime.value || !args->privkey.ec.params.coefficientA.value || !args->privkey.ec.params.coefficientB.value || !args->privkey.ec.params.basePointG.value || !args->privkey.ec.params.order.value || !args->privkey.ec.params.coFactor.value ) { LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "Missing information about EC private key."); } /* Calculate the length of all inner tag-length-value entries, but do not write anything yet. */ tags_len = 0; r = sc_asn1_put_tag(0x81, NULL, args->privkey.ec.params.prime.len, NULL, 0, NULL); LOG_TEST_RET(card->ctx, r, "Error handling TLV."); tags_len += r; r = sc_asn1_put_tag(0x82, NULL, args->privkey.ec.params.coefficientA.len, NULL, 0, NULL); LOG_TEST_RET(card->ctx, r, "Error handling TLV."); tags_len += r; r = sc_asn1_put_tag(0x83, NULL, args->privkey.ec.params.coefficientB.len, NULL, 0, NULL); LOG_TEST_RET(card->ctx, r, "Error handling TLV."); tags_len += r; r = sc_asn1_put_tag(0x84, NULL, args->privkey.ec.params.basePointG.len, NULL, 0, NULL); LOG_TEST_RET(card->ctx, r, "Error handling TLV."); tags_len += r; r = sc_asn1_put_tag(0x85, NULL, args->privkey.ec.params.order.len, NULL, 0, NULL); LOG_TEST_RET(card->ctx, r, "Error handling TLV."); tags_len += r; r = sc_asn1_put_tag(0x87, NULL, args->privkey.ec.params.coFactor.len, NULL, 0, NULL); LOG_TEST_RET(card->ctx, r, "Error handling TLV."); tags_len += r; r = sc_asn1_put_tag(0x88, NULL, args->privkey.ec.privateD.len, NULL, 0, NULL); LOG_TEST_RET(card->ctx, r, "Error handling TLV."); tags_len += r; /* Write the outer tag and length information. */ p = sbuf; r = sc_asn1_put_tag(0xE0, NULL, tags_len, p, sizeof(sbuf), &p); LOG_TEST_RET(card->ctx, r, "Error handling TLV."); /* Write inner tags. */ r = isoApplet_put_ec_params(card, &args->privkey.ec.params, p, sizeof(sbuf) - (p - sbuf), &p); if(r < 0) { sc_log(card->ctx, "Error composing EC params."); goto out; } r = sc_asn1_put_tag(0x88, args->privkey.ec.privateD.value, args->privkey.ec.privateD.len, p, sizeof(sbuf) - (p - sbuf), &p); if(r < 0) goto out; /* Send to card. */ sc_format_apdu(card, &apdu, SC_APDU_CASE_3, 0xDB, 0x3F, 0xFF); 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) { sc_log(card->ctx, "APDU transmit failed"); goto out; } r = sc_check_sw(card, apdu.sw1, apdu.sw2); if(apdu.sw1 == 0x6D && apdu.sw2 == 0x00) { sc_log(card->ctx, "The applet returned that the PUT DATA instruction byte is not supported. " "If you are using an older applet version and are trying to import keys, please update your applet first."); } else if(apdu.sw1 == 0x6A && apdu.sw2 == 0x81) { sc_log(card->ctx, "Key import not supported by the card with that particular key type. " "Your card may not support the specified algorithm used by the applet / specified by you. " "In most cases, this happens when trying to import EC keys not supported by your java card. " "In this case, look for supported field lengths and whether FP and/or F2M are supported. " "If you tried to import a private RSA key, check the key length."); } else if(apdu.sw1 == 0x69 && apdu.sw2 == 0x00) { sc_log(card->ctx, "Key import not allowed by the applet's security policy. " "If you want to allow key import, set DEF_PRIVATE_KEY_IMPORT_ALLOWED in the IsoApplet," " rebuild and reinstall the applet."); } if(r < 0) { sc_log(card->ctx, "Card returned error"); goto out; } r = SC_SUCCESS; out: sc_mem_clear(sbuf, sizeof(sbuf)); LOG_FUNC_RETURN(card->ctx, r); } /* * @brief Import a private key. */ 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; LOG_FUNC_CALLED(card->ctx); /* * Private keys are not stored in the filesystem. * ISO 7816-8 - section C.2 describes: * "Usage of the PUT DATA command for private key import" * The applet uses this PUT DATA to import private keys, if private key import is allowed. * * 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 KEY PAIR). */ /* MANAGE SECURITY ENVIRONMENT (SET). Set the algorithm and key references. */ sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0x00); p = sbuf; *p++ = 0x80; /* algorithm reference */ *p++ = 0x01; *p++ = args->algorithm_ref; *p++ = 0x84; /* Private key reference */ *p++ = 0x01; *p++ = args->priv_key_ref; sz = p - sbuf; p = NULL; apdu.lc = sz; apdu.datalen = sz; apdu.data = sbuf; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "%s: APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(card->ctx, r, "Card returned error"); /* PUT DATA */ switch(args->algorithm_ref) { case SC_ISOAPPLET_ALG_REF_RSA_GEN_2048: r = isoApplet_put_data_prkey_rsa(card, args); LOG_TEST_RET(card->ctx, r, "Error in PUT DATA."); break; case SC_ISOAPPLET_ALG_REF_EC_GEN: r = isoApplet_put_data_prkey_ec(card, args); LOG_TEST_RET(card->ctx, r, "Error in PUT DATA."); break; default: LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED, "Unknown algorithm reference."); } LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } static int isoApplet_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) { int r = 0; LOG_FUNC_CALLED(card->ctx); switch (cmd) { case SC_CARDCTL_ISOAPPLET_GENERATE_KEY: r = isoApplet_ctl_generate_key(card, (sc_cardctl_isoApplet_genkey_t *) ptr); break; case SC_CARDCTL_ISOAPPLET_IMPORT_KEY: r = isoApplet_ctl_import_key(card, (sc_cardctl_isoApplet_import_key_t *) ptr); break; default: r = SC_ERROR_NOT_SUPPORTED; } LOG_FUNC_RETURN(card->ctx, r); } static int isoApplet_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; size_t sz; struct isoApplet_drv_data *drvdata = DRVDATA(card); LOG_FUNC_CALLED(card->ctx); 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) { case SC_SEC_OPERATION_DECIPHER: apdu.p2 = 0xB8; break; case SC_SEC_OPERATION_SIGN: apdu.p2 = 0xB6; break; default: return SC_ERROR_INVALID_ARGUMENTS; } p = sbuf; if (env->flags & SC_SEC_ENV_ALG_PRESENT) { switch(env->algorithm) { case SC_ALGORITHM_RSA: if( env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1 ) { 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 does not support requested padding/hash combination"); } break; case SC_ALGORITHM_EC: drvdata->sec_env_alg_ref = ISOAPPLET_ALG_REF_ECDSA; drvdata->sec_env_ec_field_length = env->algorithm_ref; break; default: LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED, "Unsupported algorithm."); } *p++ = 0x80; /* algorithm reference */ *p++ = 0x01; *p++ = drvdata->sec_env_alg_ref; } if (env->flags & SC_SEC_ENV_FILE_REF_PRESENT) { *p++ = 0x81; *p++ = env->file_ref.len; assert(sizeof(sbuf) - (p - sbuf) >= 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_SYMMETRIC) *p++ = 0x83; else *p++ = 0x84; *p++ = env->key_ref_len; assert(sizeof(sbuf) - (p - sbuf) >= env->key_ref_len); memcpy(p, env->key_ref, env->key_ref_len); p += env->key_ref_len; } sz = p - sbuf; apdu.lc = sz; apdu.datalen = sz; apdu.data = sbuf; r = (int)sz; if (apdu.datalen != 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, "Card returned error"); } LOG_FUNC_RETURN(card->ctx, r); } static int isoApplet_compute_signature(struct sc_card *card, const u8 *data, size_t datalen, u8 *out, size_t outlen) { struct sc_context *ctx = card->ctx; struct isoApplet_drv_data *drvdata = DRVDATA(card); int r; LOG_FUNC_CALLED(ctx); 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 = (drvdata->sec_env_ec_field_length + 7) / 8 * 2; if (len > outlen) LOG_FUNC_RETURN(ctx, SC_ERROR_BUFFER_TOO_SMALL); 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); } static int isoApplet_get_challenge(struct sc_card *card, u8 *rnd, size_t len) { int r; LOG_FUNC_CALLED(card->ctx); if(card->caps & SC_CARD_CAP_RNG) { r = iso_ops->get_challenge(card, rnd, len); } else { r = SC_ERROR_NOT_SUPPORTED; } 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 = isoApplet_select_applet(card, isoApplet_aid, sizeof(isoApplet_aid)); } LOG_FUNC_RETURN(card->ctx, r); } static int isoApplet_logout(sc_card_t *card) { return isoApplet_select_applet(card, isoApplet_aid, sizeof(isoApplet_aid)); } static struct sc_card_driver *sc_get_driver(void) { sc_card_driver_t *iso_drv = sc_get_iso7816_driver(); if(iso_ops == NULL) { iso_ops = iso_drv->ops; } isoApplet_ops = *iso_drv->ops; isoApplet_ops.match_card = isoApplet_match_card; isoApplet_ops.init = isoApplet_init; isoApplet_ops.finish = isoApplet_finish; isoApplet_ops.card_ctl = isoApplet_card_ctl; isoApplet_ops.create_file = isoApplet_create_file; isoApplet_ops.process_fci = isoApplet_process_fci; 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; isoApplet_ops.read_record = NULL; isoApplet_ops.write_record = NULL; isoApplet_ops.append_record = NULL; isoApplet_ops.update_record = NULL; isoApplet_ops.restore_security_env = NULL; return &isoApplet_drv; } struct sc_card_driver * sc_get_isoApplet_driver(void) { return sc_get_driver(); } OpenSC-0.26.1/src/libopensc/card-itacns.c000066400000000000000000000332631474147347300200550ustar00rootroot00000000000000/* * card-itacns.c: Support for Italian CNS * * Copyright (C) 2008-2010 Emanuele Pucciarelli * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* * Specifications for the development of this driver come from: * http://www.cnipa.gov.it/html/docs/CNS%20Functional%20Specification%201.1.5_11012010.pdf */ #include "internal.h" #include "cardctl.h" #include "itacns.h" #include #include #include #define ITACNS_MAX_PAYLOAD 0xff static const struct sc_card_operations *default_ops = NULL; static struct sc_card_operations itacns_ops; static struct sc_card_driver itacns_drv = { "Italian CNS", "itacns", &itacns_ops, NULL, 0, NULL }; /* * Card matching */ /* List of ATR's for "hard" matching. */ 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} }; /* Macro to access private driver data. */ #define DRVDATA(card) ((itacns_drv_data_t *) card->drv_data) static void itacns_init_cns_card(sc_card_t *card) { if (15 != card->reader->atr_info.hist_bytes_len) return; 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; /* Warn if version is not 1.X. */ if (cns_version != 0x10 && cns_version != 0x11) { char version[8]; 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); } } static int itacns_match_cns_card(sc_card_t *card) { u8 manufacturer_code; u8 manufacturer_mask; u8 fw_major; 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) { 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) { int r = 0; /* Try table first */ r = _sc_match_atr(card, itacns_atrs, &card->type); if(r >= 0) 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; } /* * Initialization and termination */ static int itacns_init(sc_card_t *card) { unsigned long flags; SC_FUNC_CALLED(card->ctx, 1); card->name = "CNS card"; card->cla = 0x00; card->drv_data = calloc(1, sizeof(itacns_drv_data_t)); if (!card->drv_data) return SC_ERROR_OUT_OF_MEMORY; 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 ; 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; } static int itacns_finish(struct sc_card *card) { if(card->drv_data) { free(card->drv_data); } return 0; } /* * Restore the indicated SE */ static int itacns_restore_security_env(sc_card_t *card, int se_num) { sc_apdu_t apdu; int r; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; SC_FUNC_CALLED(card->ctx, 1); /* * The Italian CNS requires a 0-valued Lc byte at the end of the APDU * (see paragraph 13.14 of the Functional Specification), but since * it is invalid, we "cheat" and pretend it's a Le byte. * * For this workaround, we must allocate and supply a response buffer, * even though we know it will not be used (we don't even check it). */ sc_format_apdu(card, &apdu, SC_APDU_CASE_2, 0x22, 0xF3, se_num); apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = 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, "Card returned error"); SC_FUNC_RETURN(card->ctx, 1, 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 itacns_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; /* Do not complain about se_num; the argument is part of the API. */ (void) se_num; assert(card != NULL && env != NULL); 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; } key_id = env->key_ref[0]; /* CIE v1 cards need to restore security environment 0x30; all the others so far want 0x03. */ r = itacns_restore_security_env(card, (card->type == SC_CARD_TYPE_ITACNS_CIE_V1 ? 0x30 : 0x03)); 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) { case SC_SEC_OPERATION_DECIPHER: apdu.p2 = 0xB8; break; case SC_SEC_OPERATION_SIGN: apdu.p2 = 0xB6; break; default: return SC_ERROR_INVALID_ARGUMENTS; } sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Setting sec env for key_id=%d\n", key_id); data[0] = 0x83; data[1] = 0x01; data[2] = key_id; apdu.lc = apdu.datalen = 3; apdu.data = 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, "Card returned error"); SC_FUNC_RETURN(card->ctx, 1, r); } /* * The 0x80 thing tells the card it's okay to search parent * directories as well for the referenced object. * This is necessary for some Italian CNS cards, and to be avoided * for others. Right now it seems that it is only needed with * cards by STIncard. */ static int itacns_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left) { data->flags |= SC_PIN_CMD_NEED_PADDING; /* Enable backtracking for STIncard cards. */ if(DRVDATA(card)->mask_manufacturer_code == ITACNS_MASKMAN_STINCARD) { 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 default_ops->pin_cmd(card, data, tries_left); } static int itacns_read_binary(sc_card_t *card, unsigned int idx, u8 *buf, size_t count, unsigned long *flags) { size_t already_read = 0; 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, (unsigned)(idx + already_read), &buf[already_read], requested, flags); if(r < 0) return r; already_read += r; if (r == 0 || (size_t)r < requested || already_read == count) { /* We have finished */ return (int)already_read; } } } static int itacns_list_files(sc_card_t *card, u8 *buf, size_t buflen) { struct sc_card_operations *list_ops; if (DRVDATA(card) && (DRVDATA(card)->mask_manufacturer_code == ITACNS_MASKMAN_SIEMENS)) { list_ops = sc_get_cardos_driver()->ops; } else { // incrypto34 no longer supported return SC_ERROR_NO_CARD_SUPPORT; } return list_ops->list_files(card, buf, buflen); } 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: case 0x66: method = SC_AC_NEVER; break; default: if (byte > 0x1F) { method = SC_AC_UNKNOWN; } else { method = SC_AC_CHV; key_ref = byte; } break; } sc_file_add_acl_entry(file, op, method, key_ref); } static const int df_acl[9] = { -1, /* LCYCLE (life cycle change) */ SC_AC_OP_UPDATE, /* UPDATE Objects */ SC_AC_OP_WRITE, /* APPEND Objects */ SC_AC_OP_INVALIDATE, /* DF */ SC_AC_OP_REHABILITATE, /* DF */ SC_AC_OP_DELETE, /* DF */ SC_AC_OP_WRITE, /* 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 */ SC_AC_OP_ERASE, /* ADMIN EF (modify meta information?) */ -1, /* INC (-> cyclic 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 itacns_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 = default_ops->select_file(card, in_path, file); if (r >= 0 && file) { parse_sec_attr((*file), (*file)->sec_attr, (*file)->sec_attr_len); } LOG_FUNC_RETURN(card->ctx, r); } static int itacns_get_serialnr(sc_card_t *card, sc_serial_number_t *serial) { sc_path_t path; sc_file_t *file; size_t len; int r; u8 rbuf[256]; 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_log(card->ctx, "Reading EF_IDCarta.\n"); sc_format_path("3F0010001003", &path); r = sc_select_file(card, &path, &file); if (r != SC_SUCCESS) { 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 //do not return FCI/FCP templates that include the file size. //Notify abnormal length anyway. if (len != 16) { sc_log(card->ctx, "Unexpected file length of EF_IDCarta (%lu)\n", (unsigned long) len); } r = sc_read_binary(card, 0, rbuf, 256, 0); if ( r != 16 ) { return SC_ERROR_WRONG_CARD; } /* cache serial number */ memcpy(card->serialnr.value, rbuf, 16); card->serialnr.len = 16; /* copy and return serial number */ memcpy(serial, &card->serialnr, sizeof(*serial)); return SC_SUCCESS; } static int itacns_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) { switch (cmd) { case SC_CARDCTL_GET_SERIALNR: return itacns_get_serialnr(card, 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) default_ops = sc_get_iso7816_driver()->ops; itacns_ops = *default_ops; itacns_ops.match_card = itacns_match_card; itacns_ops.init = itacns_init; itacns_ops.finish = itacns_finish; itacns_ops.set_security_env = itacns_set_security_env; itacns_ops.restore_security_env = itacns_restore_security_env; itacns_ops.pin_cmd = itacns_pin_cmd; itacns_ops.read_binary = itacns_read_binary; 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; } struct sc_card_driver * sc_get_itacns_driver(void) { return sc_get_driver(); } OpenSC-0.26.1/src/libopensc/card-jpki.c000066400000000000000000000242711474147347300175300ustar00rootroot00000000000000/* * card-jpki.c: Support for JPKI(Japanese Individual Number Cards). * * Copyright (C) 2016, HAMANO Tsukasa * * 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 "jpki.h" 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} }; static struct sc_card_operations jpki_ops; static struct sc_card_driver jpki_drv = { "JPKI(Japanese Individual Number Cards)", "jpki", &jpki_ops, NULL, 0, NULL }; int jpki_select_ap(struct sc_card *card) { int rc; sc_path_t path; LOG_FUNC_CALLED(card->ctx); /* Select JPKI application */ sc_format_path(AID_JPKI, &path); path.type = SC_PATH_TYPE_DF_NAME; rc = sc_select_file(card, &path, NULL); LOG_TEST_RET(card->ctx, rc, "select JPKI AP failed"); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } static int jpki_match_card(struct sc_card *card) { int i, rc; i = _sc_match_atr(card, jpki_atrs, &card->type); if (i >= 0) { return 1; } rc = jpki_select_ap(card); if (rc == SC_SUCCESS) { card->type = SC_CARD_TYPE_JPKI_BASE; return 1; } return 0; } static int jpki_finish(sc_card_t * card) { struct jpki_private_data *drvdata = JPKI_DRVDATA(card); LOG_FUNC_CALLED(card->ctx); if (drvdata) { if (drvdata->mf) { free(drvdata->mf); } free(drvdata); card->drv_data = NULL; } LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } static int jpki_init(struct sc_card *card) { struct jpki_private_data *drvdata; sc_file_t *mf; int flags; LOG_FUNC_CALLED(card->ctx); drvdata = malloc(sizeof (struct jpki_private_data)); if (!drvdata) LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); memset(drvdata, 0, sizeof (struct jpki_private_data)); /* create virtual MF */ mf = sc_file_new(); if (!mf) { free(drvdata); LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); } sc_format_path("3f00", &mf->path); mf->type = SC_FILE_TYPE_DF; mf->shareable = 0; mf->ef_structure = SC_FILE_EF_UNKNOWN; mf->size = 0; mf->id = 0x3f00; mf->status = SC_FILE_STATUS_ACTIVATED; sc_file_add_acl_entry(mf, SC_AC_OP_SELECT, SC_AC_NONE, 0); sc_file_add_acl_entry(mf, SC_AC_OP_LIST_FILES, SC_AC_NONE, 0); sc_file_add_acl_entry(mf, SC_AC_OP_LOCK, SC_AC_NEVER, 0); sc_file_add_acl_entry(mf, SC_AC_OP_DELETE, SC_AC_NEVER, 0); sc_file_add_acl_entry(mf, SC_AC_OP_CREATE, SC_AC_NEVER, 0); drvdata->mf = mf; drvdata->selected = SELECT_MF; card->name = "jpki"; card->drv_data = drvdata; flags = SC_ALGORITHM_RSA_HASH_NONE | SC_ALGORITHM_RSA_PAD_PKCS1; _sc_card_add_rsa_alg(card, 2048, flags, 0); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } static int jpki_select_file(struct sc_card *card, const struct sc_path *path, struct sc_file **file_out) { struct jpki_private_data *drvdata = JPKI_DRVDATA(card); int rc; sc_apdu_t apdu; struct sc_file *file = NULL; LOG_FUNC_CALLED(card->ctx); sc_log(card->ctx, "jpki_select_file: path=%s, len=%"SC_FORMAT_LEN_SIZE_T"u", sc_print_path(path), path->len); if (path->len == 2 && memcmp(path->value, "\x3F\x00", 2) == 0) { drvdata->selected = SELECT_MF; if (file_out) { sc_file_dup(file_out, drvdata->mf); if (*file_out == NULL) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); } } return 0; } sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 0, 0); switch (path->type) { case SC_PATH_TYPE_FILE_ID: apdu.p1 = 2; break; case SC_PATH_TYPE_DF_NAME: apdu.p1 = 4; break; default: LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } apdu.p2 = 0x0C; apdu.data = path->value; apdu.datalen = path->len; apdu.lc = path->len; rc = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, rc, "APDU transmit failed"); rc = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(card->ctx, rc, "SW Check failed"); if (!file_out) { LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } /* read certificate file size */ if (path->len == 2 && ( memcmp(path->value, "\x00\x0A", 2) == 0 || memcmp(path->value, "\x00\x01", 2) == 0 || memcmp(path->value, "\x00\x0B", 2) == 0 || memcmp(path->value, "\x00\x02", 2) == 0 ) ) { 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); } file->path = *path; file->size = (buf[2] << 8 | buf[3]) + 4; *file_out = file; } LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } static int jpki_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left) { int rc; sc_path_t path; sc_apdu_t apdu; struct jpki_private_data *priv = JPKI_DRVDATA(card); int max_tries = 0; LOG_FUNC_CALLED(card->ctx); if (tries_left) { *tries_left = -1; } switch (data->pin_reference) { case 1: sc_format_path(JPKI_AUTH_PIN, &path); path.type = SC_PATH_TYPE_FILE_ID; rc = sc_select_file(card, &path, NULL); max_tries = JPKI_AUTH_PIN_MAX_TRIES; break; case 2: sc_format_path(JPKI_SIGN_PIN, &path); path.type = SC_PATH_TYPE_FILE_ID; rc = sc_select_file(card, &path, NULL); max_tries = JPKI_SIGN_PIN_MAX_TRIES; break; default: sc_log(card->ctx, "Unknown PIN reference: %d", data->pin_reference); LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } LOG_TEST_RET(card->ctx, rc, "SELECT_FILE error"); 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; apdu.lc = data->pin1.len; rc = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, rc, "APDU transmit failed"); rc = sc_check_sw(card, apdu.sw1, apdu.sw2); if (rc == SC_SUCCESS) { data->pin1.logged_in = SC_PIN_STATE_LOGGED_IN; data->pin1.tries_left = max_tries; } else { data->pin1.logged_in = SC_PIN_STATE_LOGGED_OUT; data->pin1.tries_left = apdu.sw2 & 0xF; } priv->logged_in = data->pin1.logged_in; LOG_TEST_RET(card->ctx, rc, "VERIFY failed"); break; case SC_PIN_CMD_GET_INFO: sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x20, 0x00, 0x80); rc = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, rc, "APDU transmit failed"); if (apdu.sw1 != 0x63) { sc_log(card->ctx, "VERIFY GET_INFO error"); LOG_FUNC_RETURN(card->ctx, SC_ERROR_CARD_CMD_FAILED); } 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); LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } static int jpki_set_security_env(sc_card_t * card, const sc_security_env_t * env, int se_num) { int rc; sc_path_t path; LOG_FUNC_CALLED(card->ctx); sc_log(card->ctx, "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); switch (env->operation) { case SC_SEC_OPERATION_SIGN: break; default: LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } switch (env->key_ref[0]) { case 1: sc_format_path(JPKI_AUTH_KEY, &path); break; case 2: sc_format_path(JPKI_SIGN_KEY, &path); break; default: LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } path.type = SC_PATH_TYPE_FILE_ID; rc = sc_select_file(card, &path, NULL); LOG_TEST_RET(card->ctx, rc, "select key failed"); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } static int jpki_compute_signature(sc_card_t * card, const u8 * data, size_t datalen, u8 * out, size_t outlen) { int rc; sc_apdu_t apdu; unsigned char resp[SC_MAX_APDU_BUFFER_SIZE]; LOG_FUNC_CALLED(card->ctx); sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x00, 0x80); apdu.cla = 0x80; apdu.data = data; apdu.datalen = datalen; apdu.lc = datalen; apdu.resp = resp; apdu.resplen = sizeof(resp); apdu.le = 0; rc = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, rc, "APDU transmit failed"); rc = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(card->ctx, rc, "SW Check failed"); if (apdu.resplen > outlen) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); } memcpy(out, resp, 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 * sc_get_driver(void) { struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); jpki_ops = *iso_drv->ops; jpki_ops.match_card = jpki_match_card; jpki_ops.init = jpki_init; jpki_ops.finish = jpki_finish; jpki_ops.select_file = jpki_select_file; 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; } struct sc_card_driver * sc_get_jpki_driver(void) { return sc_get_driver(); } OpenSC-0.26.1/src/libopensc/card-masktech.c000066400000000000000000000265171474147347300203770ustar00rootroot00000000000000/* * card-masktech.c: Support for Masktech smart cards using the MTCOS operating system. * * Copyright (C) 2011-2015 MaskTech GmbH Fischerstrasse 19, 87435 Kempten, Germany * Copyright (C) 2011 Andrey Uvarov (X-Infotech) * Copyright (C) 2015 Vincent Le Toux (My Smart Logon) * * 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 "iso7816.h" static struct sc_atr_table masktech_atrs[] = { {"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", "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} }; static struct sc_card_operations *iso_ops; static struct sc_card_operations masktech_ops; static struct sc_card_driver masktech_drv = { "MaskTech Smart Card", "MaskTech", &masktech_ops, masktech_atrs, 0, NULL }; struct masktech_private_data { /* save the key reference set at set_masktech_set_security_env to recover it as the signature step */ int rsa_key_ref; }; static int masktech_match_card(sc_card_t * card) { /* check if the ATR is in the known ATR */ if (_sc_match_atr(card, masktech_atrs, &card->type) < 0) return 0; return 1; } static int masktech_init(sc_card_t * card) { unsigned long flags; struct masktech_private_data *data; 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)))) return SC_ERROR_OUT_OF_MEMORY; card->drv_data = data; /* supported RSA keys and how padding is done */ flags = SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_NONE; _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); card->caps |= SC_CARD_CAP_APDU_EXT; return SC_SUCCESS; } static int masktech_finish(sc_card_t *card) { /* free the private data */ if (card->drv_data) { free(card->drv_data); card->drv_data = NULL; } return 0; } static int masktech_set_security_env(sc_card_t *card, const sc_security_env_t *env, int se_num) { struct masktech_private_data *private_data; 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; if (!private_data) return SC_ERROR_INTERNAL; /* save the key reference */ if (env->flags & SC_SEC_ENV_KEY_REF_PRESENT) { if (env->key_ref_len != 1) { sc_log(card->ctx, "Invalid key reference supplied.\n"); return SC_ERROR_NOT_SUPPORTED; } private_data->rsa_key_ref = env->key_ref[0]; } return iso_ops->set_security_env(card, env, se_num); } static int masktech_compute_signature(sc_card_t *card, const u8 * data, size_t datalen, u8 * out, size_t outlen) { struct masktech_private_data *private_data; u8 sha256hash[32]; static const u8 hdr_sha256[] = { 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_log(card->ctx, "masktech_compute_signature()\n"); /* retrieve the key reference */ private_data = (struct masktech_private_data *) card->drv_data; if (!private_data) return SC_ERROR_INTERNAL; if (private_data->rsa_key_ref == 0x88) { /* for this key reference, the card supports only SHA256 hash and the hash is computed using a digest info */ /* check that it is a SHA256 with digest info*/ if ((datalen != sizeof(hdr_sha256) + 32) || (memcmp(hdr_sha256, data, sizeof(hdr_sha256)) != 0)) { sc_log(card->ctx, "It is not a SHA256 with digestinfo\n"); return SC_ERROR_NOT_SUPPORTED; } /* extract the SHA-256 hash */ memcpy(sha256hash, (u8 *)(data+(datalen-32)), 32);//only last 32 byte => sha256 /* default ISO 7816 functions */ return iso_ops->compute_signature(card, sha256hash, 32, out, outlen); } else { /* default ISO 7816 functions */ return iso_ops->compute_signature(card, data, datalen, out, outlen); } } static int masktech_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_EXT_APDU_BUFFER_SIZE]; assert(card != NULL && crgram != NULL && out != NULL); 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); } sc_format_apdu(card, &apdu, SC_APDU_CASE_4_EXT, 0x2A, 0x80, 0x86); apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); /* the card doesn't support anything else here (+1 / -1 is not working) */ apdu.le = 65536; apdu.data = crgram; apdu.lc = crgram_len; apdu.datalen = crgram_len; 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); 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) { int rv = 0; struct sc_pin_cmd_data verify_data; struct sc_pin_cmd_data reset_data; /* Build a SC_PIN_CMD_VERIFY APDU on PUK */ memset(&verify_data, 0, sizeof(verify_data)); verify_data.cmd = SC_PIN_CMD_VERIFY; verify_data.pin_type = 1; verify_data.pin_reference = 0x83; verify_data.pin1 = data->pin1; verify_data.flags = data->flags; verify_data.pin1.prompt = data->pin1.prompt; rv = iso_ops->pin_cmd(card, &verify_data, tries_left); 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)); reset_data.cmd = SC_PIN_CMD_UNBLOCK; reset_data.pin_type = 1; reset_data.pin_reference = 0x91; /* pin1 is set to null on purpose and flag set to implicit change => if there is a pinpad reader, do not ask for pin1 */ reset_data.pin2 = data->pin2; 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); 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) { int rv = 0; struct sc_pin_cmd_data verify_data; struct sc_pin_cmd_data change_data; /* Build a SC_PIN_CMD_VERIFY APDU */ memset(&verify_data, 0, sizeof(verify_data)); verify_data.cmd = SC_PIN_CMD_VERIFY; verify_data.pin_type = 1; verify_data.pin_reference = data->pin_reference; verify_data.pin1 = data->pin1; verify_data.flags = data->flags; verify_data.pin1.prompt = data->pin1.prompt; rv = iso_ops->pin_cmd(card, &verify_data, tries_left); 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)); change_data.cmd = SC_PIN_CMD_CHANGE; change_data.pin_type = 1; change_data.pin_reference = data->pin_reference; /* pin1 is set to null on purpose and flag set to implicit change => if there is a pinpad reader, do not ask for pin1 */ change_data.pin2 = data->pin2; 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); 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) { int rv; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); switch(data->cmd) { case SC_PIN_CMD_UNBLOCK: rv = masktech_pin_unblock(card, data, tries_left); break; case SC_PIN_CMD_CHANGE: rv = masktech_pin_change(card, data, tries_left); break; default: rv = iso_ops->pin_cmd(card, data, tries_left); break; } return rv; } static int masktech_get_serialnr(sc_card_t * card, sc_serial_number_t * serial) { struct sc_apdu apdu; unsigned char apdu_resp[SC_MAX_APDU_BUFFER_SIZE-2]; int rv; if (!serial) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); /* Get smart card serial number */ card->cla = 0x80; sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x08, 0x00, 0x00); apdu.resplen = sizeof(apdu_resp); apdu.resp = apdu_resp; rv = sc_transmit_apdu(card, &apdu); card->cla = 0x00; 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) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); } /* cache serial number */ card->serialnr.len = apdu.resplen; memcpy(card->serialnr.value, apdu.resp, card->serialnr.len); /* copy and return serial number */ if (serial) memcpy(serial, &card->serialnr, sizeof(*serial)); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } static int masktech_card_ctl(sc_card_t * card, unsigned long cmd, void *ptr) { 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); default: return SC_ERROR_NOT_SUPPORTED; } } static struct sc_card_driver *sc_get_driver(void) { if (iso_ops == NULL) iso_ops = sc_get_iso7816_driver()->ops; masktech_ops = *iso_ops; masktech_ops.match_card = masktech_match_card; masktech_ops.init = masktech_init; masktech_ops.finish = masktech_finish; masktech_ops.set_security_env = masktech_set_security_env; masktech_ops.compute_signature = masktech_compute_signature; masktech_ops.decipher = masktech_decipher; masktech_ops.pin_cmd = masktech_pin_cmd; masktech_ops.card_ctl = masktech_card_ctl; return &masktech_drv; } struct sc_card_driver *sc_get_masktech_driver(void) { return sc_get_driver(); } OpenSC-0.26.1/src/libopensc/card-mcrd.c000066400000000000000000000713651474147347300175260ustar00rootroot00000000000000/* * card-mcrd.c: Support for MICARDO cards * * Copyright (C) 2004 Martin Paljak * Copyright (C) 2004 Priit Randla * Copyright (C) 2003 Marie Fischer * Copyright (C) 2001 Juha Yrjölä * Copyright (C) 2002 g10 Code GmbH * * 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 "asn1.h" #include "cardctl.h" #include "gp.h" 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_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_GENERIC, 0, NULL}, {NULL, NULL, NULL, 0, 0, NULL} }; static struct sc_card_operations mcrd_ops; static struct sc_card_driver mcrd_drv = { "MICARDO 2.1", "mcrd", &mcrd_ops, NULL, 0, NULL }; static const struct sc_card_operations *iso_ops = NULL; enum { MCRD_SEL_MF = 0x00, MCRD_SEL_DF = 0x01, MCRD_SEL_EF = 0x02, MCRD_SEL_PARENT = 0x03, MCRD_SEL_AID = 0x04 }; #define MFID 0x3F00 #define EF_KeyD 0x0013 /* File with extra key information. */ #define EF_Rule 0x0030 /* Default ACL file. */ #define MAX_CURPATH 10 struct rule_record_s { struct rule_record_s *next; unsigned int recno; size_t datalen; u8 data[1]; }; struct keyd_record_s { struct keyd_record_s *next; unsigned int recno; size_t datalen; u8 data[1]; }; struct df_info_s { struct df_info_s *next; unsigned short path[MAX_CURPATH]; size_t pathlen; struct rule_record_s *rule_file; /* keeps records of EF_Rule. */ struct keyd_record_s *keyd_file; /* keeps records of EF_KeyD. */ }; struct mcrd_priv_data { unsigned short curpath[MAX_CURPATH]; /* The currently selected path. */ 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)) 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); /* Return the DF_info for the current path. If does not yet exist, create it. Returns NULL on error. */ static struct df_info_s *get_df_info(sc_card_t * card) { sc_context_t *ctx = card->ctx; struct mcrd_priv_data *priv = DRVDATA(card); struct df_info_s *dfi; if(!(!priv->is_ef)) return NULL; if (!priv->curpathlen) { 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)) return dfi; } /* Not found, create it. */ dfi = calloc(1, sizeof *dfi); if (!dfi) { sc_log(ctx, "out of memory while allocating df_info\n"); return NULL; } dfi->pathlen = priv->curpathlen; memcpy(dfi->path, priv->curpath, dfi->pathlen * sizeof *dfi->path); dfi->next = priv->df_infos; priv->df_infos = dfi; return dfi; } static void clear_special_files(struct df_info_s *dfi) { if (dfi) { while (dfi->rule_file) { struct rule_record_s *tmp = dfi->rule_file->next; free(dfi->rule_file); dfi->rule_file = tmp; } while (dfi->keyd_file) { struct keyd_record_s *tmp = dfi->keyd_file->next; free(dfi->keyd_file); dfi->keyd_file = tmp; } } } /* * Official notice: Refer to the Micardo 2.1 Public manual. * Sad side: not available without a NDA. */ static int mcrd_delete_ref_to_authkey(sc_card_t * card) { sc_apdu_t apdu; int r; 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); 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 int mcrd_delete_ref_to_signkey(sc_card_t * card) { sc_apdu_t apdu; int r; 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); 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 int mcrd_match_card(sc_card_t * card) { int i = 0; i = _sc_match_atr(card, mcrd_atrs, &card->type); if (i >= 0) { card->name = mcrd_atrs[i].name; return 1; } return 0; } static int mcrd_init(sc_card_t * card) { unsigned long flags; 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 (SC_SUCCESS != sc_select_file (card, sc_get_mf_path(), NULL)) sc_log(card->ctx, "Warning: select MF failed"); load_special_files(card); return SC_SUCCESS; } static int mcrd_finish(sc_card_t * card) { struct mcrd_priv_data *priv; if (card == NULL) return 0; priv = DRVDATA(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); return 0; } /* Load the rule and keyd file into our private data. Return 0 on success */ static int load_special_files(sc_card_t * card) { sc_context_t *ctx = card->ctx; int r; unsigned int recno; struct df_info_s *dfi; struct rule_record_s *rule; struct keyd_record_s *keyd; /* First check whether we already cached it. */ dfi = get_df_info(card); if (dfi && dfi->rule_file) return 0; /* yes. */ clear_special_files(dfi); if (!dfi) 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); LOG_TEST_RET(ctx, r, "selecting EF_Rule failed"); for (recno = 1;; recno++) { u8 recbuf[256]; r = sc_read_record(card, recno, 0, recbuf, sizeof(recbuf), SC_RECORD_BY_REC_NR); if (r == SC_ERROR_RECORD_NOT_FOUND) break; if (r < 0) { SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r); } else { rule = malloc(sizeof *rule + (size_t)r); if (!rule) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); rule->recno = recno; rule->datalen = (size_t)r; memcpy(rule->data, recbuf, r); rule->next = dfi->rule_file; dfi->rule_file = rule; } } 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_log(ctx, "no EF_KeyD file available\n"); return 0; /* That is okay. */ } LOG_TEST_RET(ctx, r, "selecting EF_KeyD failed"); for (recno = 1;; recno++) { u8 recbuf[256]; r = sc_read_record(card, recno, 0, recbuf, sizeof(recbuf), SC_RECORD_BY_REC_NR); if (r == SC_ERROR_RECORD_NOT_FOUND) break; if (r < 0) { SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r); } else { keyd = malloc(sizeof *keyd + (size_t)r); if (!keyd) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); keyd->recno = recno; keyd->datalen = (size_t) r; memcpy(keyd->data, recbuf, r); keyd->next = dfi->keyd_file; dfi->keyd_file = keyd; } } 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; } /* Process an ARR (7816-9/8.5.4) and setup the ACL. */ 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; struct rule_record_s *rule; size_t left, taglen; unsigned int cla, tag; const u8 *p; int skip; char dbgbuf[2048]; /* Currently we support only the short for. */ if (buflen != 1) { 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) ; if (!rule) { sc_log(ctx, "referenced EF_rule record %d not found\n", *buf); return; } sc_hex_dump(rule->data, rule->datalen, dbgbuf, sizeof dbgbuf); sc_log(ctx, "rule for record %d:\n%s", *buf, dbgbuf); p = rule->data; left = rule->datalen; skip = 1; /* Skip over initial unknown SC DOs. */ for (;;) { buf = p; if (sc_asn1_read_tag(&p, left, &cla, &tag, &taglen) != SC_SUCCESS || p == NULL) break; left -= (size_t)(p - buf); tag |= cla; if (tag == 0x80 && taglen != 1) { skip = 1; } else if (tag == 0x80) { /* AM byte. */ sc_log(ctx, " AM_DO: %02x\n", *p); skip = 0; } else if (tag >= 0x81 && tag <= 0x8f) { /* Cmd description */ 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" : "", (tag & 1) ? "2" : "", dbgbuf); skip = 0; } else if (tag == 0x9C) { /* Proprietary state machine descrip. */ skip = 1; } else if (!skip) { switch (tag) { case 0x90: /* Always */ sc_log(ctx, " SC: always\n"); break; case 0x97: /* Never */ sc_log(ctx, " SC: never\n"); break; case 0xA4: /* Authentication, value is a CRT. */ 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_log_hex(ctx, " SC: cmd/resp", p, taglen); break; case 0x9E: /* Security Condition byte. */ sc_log_hex(ctx, " SC: condition", p, taglen); break; case 0xA0: /* OR template. */ sc_log(ctx, " SC: OR\n"); break; case 0xAF: /* AND template. */ sc_log(ctx, " SC: AND\n"); break; } } left -= taglen; p += taglen; } } static void process_fcp(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; int bad_fde = 0; 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_log(ctx, " file identifier: 0x%02X%02X\n", tag[0], tag[1]); } /* Number of data bytes in the file including structural information. */ tag = sc_asn1_find_tag(ctx, p, len, 0x81, &taglen); if (!tag) { /* My card does not encode the filelength in 0x81 but in 0x85 which is the file descriptor extension in TCOS. Assume that this is the case when the regular file size tag is not encoded. */ tag = sc_asn1_find_tag(ctx, p, len, 0x85, &taglen); bad_fde = !!tag; } if (tag != NULL && taglen >= 2) { int bytes = (tag[0] << 8) + tag[1]; sc_log(ctx, " bytes in file: %d\n", 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_log(ctx, " bytes in file: %d\n", bytes); file->size = (size_t)bytes; } } /* File descriptor byte(s). */ tag = sc_asn1_find_tag(ctx, p, len, 0x82, &taglen); if (tag != NULL) { /* Fixme, this might actual be up to 6 bytes. */ if (taglen > 0) { unsigned char byte = tag[0]; const char *type; file->shareable = byte & 0x40 ? 1 : 0; sc_log(ctx, " shareable: %s\n", (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: type = "unknown"; break; } sc_log(ctx, " type: %s\n", type); sc_log(ctx, " EF structure: %d\n", byte & 0x07); } } /* DF name. */ tag = sc_asn1_find_tag(ctx, p, len, 0x84, &taglen); if (tag != NULL && taglen > 0 && taglen <= 16) { char name[17]; size_t i; memcpy(file->name, tag, taglen); file->namelen = taglen; for (i = 0; i < taglen; i++) { if (isalnum(tag[i]) || ispunct(tag[i]) || isspace(tag[i])) name[i] = (const char)tag[i]; else name[i] = '?'; } name[taglen] = 0; sc_log(ctx, " file name: %s\n", name); } /* Proprietary information. */ tag = bad_fde ? NULL : 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; /* Proprietary information, constructed. */ tag = sc_asn1_find_tag(ctx, p, len, 0xA5, &taglen); if (tag != NULL && taglen) { sc_file_set_prop_attr(file, tag, taglen); } /* Security attributes, proprietary format. */ tag = sc_asn1_find_tag(ctx, p, len, 0x86, &taglen); if (tag != NULL && taglen) { sc_file_set_sec_attr(file, tag, taglen); } /* Security attributes, reference to expanded format. */ tag = sc_asn1_find_tag(ctx, p, len, 0x8B, &taglen); if (tag && taglen) { process_arr(card, tag, taglen); } else if ((tag = sc_asn1_find_tag(ctx, p, len, 0xA1, &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, tag, taglen); } file->magic = SC_FILE_MAGIC; } /* Send a select command and parse the response. */ static int do_select(sc_card_t * card, u8 kind, const u8 * buf, size_t buflen, sc_file_t ** file) { sc_apdu_t apdu; u8 resbuf[SC_MAX_APDU_BUFFER_SIZE]; int r; u8 p2 = 0x00; if (kind == MCRD_SEL_EF) p2 = 0x04; if (kind == MCRD_SEL_DF) p2 = 0x0C; sc_format_apdu_ex(&apdu, 0x00, 0xA4, kind, p2, buf, buflen, resbuf, 256); r = sc_transmit_apdu(card, &apdu); 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); if (r) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); if (p2 == 0x0C) { if (file) { *file = sc_file_new(); if (!*file) LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); (*file)->type = SC_FILE_TYPE_DF; return SC_SUCCESS; } } if (p2 == 0x04 && apdu.resplen > 2 && apdu.resp[0] == 0x62) { *file = sc_file_new(); if (!*file) LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); if (apdu.resp[1] > apdu.resplen - 2) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_DATA); process_fcp(card, *file, apdu.resp + 2, apdu.resp[1]); return SC_SUCCESS; } if (p2 != 0x0C && apdu.resplen > 2 && apdu.resp[0] == 0x6F) { *file = sc_file_new(); if (!*file) LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); if (apdu.resp[1] > apdu.resplen - 2) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_DATA); process_fcp(card, *file, apdu.resp + 2, apdu.resp[1]); return SC_SUCCESS; } return SC_SUCCESS; } /* Wrapper around do_select to be used when multiple selects are required. */ static int select_part(sc_card_t * card, u8 kind, unsigned short int fid, sc_file_t ** file) { u8 fbuf[2]; unsigned int len; int r; sc_log(card->ctx, "select_part (0x%04X, kind=%u)\n", fid, kind); if (fid == MFID) { kind = MCRD_SEL_MF; /* force this kind. */ len = 0; } else { fbuf[0] = fid >> 8; fbuf[1] = fid & 0xff; len = 2; } r = do_select(card, kind, fbuf, len, file); return r; } /* Select a file by iterating over the FID in the PATHPTR array while updating the curpath kept in the private data cache. With DF_ONLY passed as true only DF are selected, otherwise the function tries 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) { struct mcrd_priv_data *priv = DRVDATA(card); int r; int found_ef = 0; if (!pathlen) return SC_ERROR_INVALID_ARGUMENTS; for (; pathlen; pathlen--, pathptr++) { if (priv->curpathlen == MAX_CURPATH) 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) found_ef = 1; } if (r) r = select_part(card, MCRD_SEL_DF, *pathptr, pathlen == 1 ? file : NULL); 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) load_special_files(card); return 0; } /* Handle the selection case when a PATH is requested. Our card does not support this addressing so we have to emulate it. To keep the security status we should not unnecessary change the directory; this is accomplished be keeping track of the currently selected file. Note that PATH is an array of PATHLEN file ids and not the usual sc_path structure. */ static int select_file_by_path(sc_card_t * card, unsigned short *pathptr, size_t pathlen, sc_file_t ** file) { struct mcrd_priv_data *priv = DRVDATA(card); int r; size_t i; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (!(!priv->curpathlen || priv->curpath[0] == MFID)) return SC_ERROR_INTERNAL; if (pathlen && *pathptr == 0x3FFF) { pathlen--; pathptr++; } if (!pathlen || pathlen >= MAX_CURPATH) r = SC_ERROR_INVALID_ARGUMENTS; else if (pathlen == 1 && pathptr[0] == MFID) { /* MF requested: clear the cache and select it. */ priv->curpathlen = 0; r = select_part(card, MCRD_SEL_MF, pathptr[0], file); LOG_TEST_RET(card->ctx, r, "unable to select MF"); priv->curpath[0] = pathptr[0]; priv->curpathlen = 1; priv->is_ef = 0; } else if (pathlen > 1 && pathptr[0] == MFID) { /* Absolute addressing, check cache to avoid unnecessary selects. */ for (i = 0; (i < pathlen && i < priv->curpathlen && pathptr[i] == priv->curpath[i]); i++) ; if (!priv->curpathlen) { /* Need to do all selects starting at the root. */ priv->curpathlen = 0; priv->is_ef = 0; r = select_down(card, pathptr, pathlen, 0, file); } else if (i == pathlen && i < priv->curpathlen) { /* Go upwards; we do it the easy way and start at the root. However we know that the target is a DF. */ priv->curpathlen = 0; priv->is_ef = 0; r = select_down(card, pathptr, pathlen, 1, file); } else if (i == pathlen && i == priv->curpathlen) { /* Already selected. */ if (!file) r = 0; /* The caller did not request the fci. */ else { /* This EF or DF was already selected, but we need to get the FCI, so we have to select again. */ if (!(priv->curpathlen > 1)) return SC_ERROR_INTERNAL; priv->curpathlen--; priv->is_ef = 0; r = select_down(card, pathptr + pathlen - 1, 1, 0, file); } } else { /* We have to append something. For now we simply start at the root. (fixme) */ priv->curpathlen = 0; priv->is_ef = 0; r = select_down(card, pathptr, pathlen, 0, file); } } else { /* Relative addressing. */ if (!priv->curpathlen) { /* Relative addressing without a current path. So we select the MF first. */ r = select_part(card, MCRD_SEL_MF, pathptr[0], file); 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) { 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; } static int select_file_by_fid(sc_card_t * card, unsigned short *pathptr, 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); if (!(!priv->curpathlen || priv->curpath[0] == MFID)) return SC_ERROR_INTERNAL; if (pathlen > 1) return SC_ERROR_INVALID_ARGUMENTS; if (pathlen && *pathptr == 0x3FFF) return 0; if (!pathlen) { /* re-select the current one if needed. */ if (!file) r = 0; /* The caller did not request the fci. */ else if (!priv->curpathlen) { /* There is no current file. */ r = SC_ERROR_INTERNAL; } else { if (!(priv->curpathlen > 1)) return SC_ERROR_INTERNAL; priv->curpathlen--; priv->is_ef = 0; r = select_down(card, pathptr, 1, 0, file); } } else if (pathptr[0] == MFID) { /* MF requested: clear the cache and select it. */ priv->curpathlen = 0; r = select_part(card, MCRD_SEL_MF, MFID, file); LOG_TEST_RET(card->ctx, r, "unable to select MF"); priv->curpath[0] = MFID; priv->curpathlen = 1; priv->is_ef = 0; } else { /* Relative addressing. */ if (!priv->curpathlen) { /* Relative addressing without a current path. So we select the MF first. */ r = select_part(card, MCRD_SEL_MF, pathptr[0], file); 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) { 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); } return r; } /* This drivers select command handler. */ static int mcrd_select_file(sc_card_t * card, const sc_path_t * path, sc_file_t ** file) { struct mcrd_priv_data *priv = DRVDATA(card); int r = 0; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (path->type == SC_PATH_TYPE_DF_NAME) { if (path->len > 16) return SC_ERROR_INVALID_ARGUMENTS; r = do_select(card, MCRD_SEL_AID, path->value, path->len, file); priv->curpathlen = 0; } else { unsigned short int pathtmp[SC_MAX_PATH_SIZE / 2]; unsigned short int *pathptr; int samepath = 1; size_t pathlen, n; 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] = (unsigned short)((path->value[n] << 8) | path->value[n + 1]); pathlen = path->len >> 1; if (pathlen == priv->curpathlen && priv->is_ef != 2) { for (n = 0; n < pathlen; n++) { if (priv->curpath[n] != pathptr[n]) { samepath = 0; break; } } } else if (priv->curpathlen < pathlen && priv->is_ef != 2) { for (n = 0; n < priv->curpathlen; n++) { if (priv->curpath[n] != pathptr[n]) { samepath = 0; break; } } pathptr = pathptr + n; pathlen = pathlen - n; } 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); else { /* SC_PATH_TYPE_FILEID */ r = select_file_by_fid(card, pathptr, pathlen, file); } } } return r; } /* It seems that MICARDO does not fully comply with ISO, so I use values gathered from peeking actual signing operations using a different system. It has been generalized [?] and modified by information coming from 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; sc_apdu_t apdu; u8 sbuf[5] = {0x83, 0x03, 0x80, 0, 0}; int r = 0, locked = 0; if (card == NULL || env == NULL) return SC_ERROR_INTERNAL; LOG_FUNC_CALLED(card->ctx); priv = DRVDATA(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; switch (env->operation) { case SC_SEC_OPERATION_DECIPHER: 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: sc_log(card->ctx, "Using keyref %d to sign\n", env->key_ref[0]); break; default: return SC_ERROR_INVALID_ARGUMENTS; } 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; } if (se_num > 0) { r = sc_lock(card); LOG_TEST_RET(card->ctx, r, "sc_lock() failed"); locked = 1; } if (apdu.datalen != 0) { 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) return 0; sc_unlock(card); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); err: if (locked) sc_unlock(card); return r; } /* heavily modified by -mp */ static int mcrd_compute_signature(sc_card_t * card, const u8 * data, size_t datalen, u8 * out, size_t outlen) { struct mcrd_priv_data *priv = DRVDATA(card); sc_security_env_t *env = NULL; int r; sc_apdu_t apdu; 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_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); 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); 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"); 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) { LOG_FUNC_CALLED(card->ctx); data->pin1.offset = 5; data->pin2.offset = 5; 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)); } static int mcrd_logout(sc_card_t * card) { return SC_ERROR_NOT_SUPPORTED; } /* Driver binding */ 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; mcrd_ops = *iso_drv->ops; mcrd_ops.match_card = mcrd_match_card; mcrd_ops.init = mcrd_init; mcrd_ops.finish = mcrd_finish; mcrd_ops.select_file = mcrd_select_file; 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; } struct sc_card_driver *sc_get_mcrd_driver(void) { return sc_get_driver(); } OpenSC-0.26.1/src/libopensc/card-muscle.c000066400000000000000000000620021474147347300200550ustar00rootroot00000000000000/* * card-muscle.c: Support for MuscleCard Applet from musclecard.com * * 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., 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 "muscle.h" #include "muscle-filesystem.h" #include "types.h" #include "opensc.h" static struct sc_card_operations muscle_ops; static const struct sc_card_operations *iso_ops = NULL; static struct sc_card_driver muscle_drv = { "MuscleApplet", "muscle", &muscle_ops, NULL, 0, NULL }; 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 */ { "3b:d5:18:00:81:31:3a:7d:80:73:c8:21:10:30", NULL, NULL, SC_CARD_TYPE_MUSCLE_ETOKEN_72K, 0, NULL }, /* JCOP31 v2.4.1 contact interface */ { "3b:f8:13:00:00:81:31:fe:45:4a:43:4f:50:76:32:34:31:b7", NULL, NULL, SC_CARD_TYPE_MUSCLE_JCOP241, 0, NULL }, /* JCOP31 v2.4.1 RF interface */ { "3b:88:80:01:4a:43:4f:50:76:32:34:31:5e", NULL, NULL, SC_CARD_TYPE_MUSCLE_JCOP241, 0, NULL }, { NULL, NULL, NULL, 0, 0, NULL } }; #define MUSCLE_DATA(card) ( (muscle_private_t*)card->drv_data ) #define MUSCLE_FS(card) ( ((muscle_private_t*)card->drv_data)->fs ) typedef struct muscle_private { sc_security_env_t env; unsigned short verifiedPins; mscfs_t *fs; int rsa_key_ref; } muscle_private_t; static int muscle_finish(sc_card_t *card) { muscle_private_t *priv = MUSCLE_DATA(card); mscfs_free(priv->fs); free(priv); return 0; } static u8 muscleAppletId[] = { 0xA0, 0x00,0x00,0x00, 0x01, 0x01 }; static int muscle_match_card(sc_card_t *card) { sc_apdu_t apdu; u8 response[64]; int r; 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); apdu.cla = 0xB0; apdu.le = 64; apdu.resplen = 64; apdu.resp = response; r = sc_transmit_apdu(card, &apdu); 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; } /* Since Musclecard has a different ACL system then PKCS15 * objects need to have their READ/UPDATE/DELETE permissions mapped for files * and directory ACLS need to be set * For keys.. they have different ACLS, but are accessed in different locations, so it shouldn't be an issue here */ static unsigned short muscle_parse_singleAcl(const sc_acl_entry_t* acl) { unsigned short acl_entry = 0; while(acl) { int key = acl->key_ref; int method = acl->method; switch(method) { case SC_AC_NEVER: return 0xFFFF; /* Ignore... other items overwrite these */ case SC_AC_NONE: case SC_AC_UNKNOWN: break; case SC_AC_CHV: /* 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: case SC_AC_PRO: default: /* Ignored */ break; } acl = acl->next; } return acl_entry; } 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); *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)); } static int muscle_create_directory(sc_card_t *card, sc_file_t *file) { mscfs_t *fs = MUSCLE_FS(card); msc_id objectId; u8* oid = objectId.id; unsigned id = file->id; unsigned short read_perm = 0, write_perm = 0, delete_perm = 0; size_t objectSize; int r; if(id == 0) /* No null name files */ return SC_ERROR_INVALID_ARGUMENTS; /* No nesting directories */ if(fs->currentPath[0] != 0x3F || fs->currentPath[1] != 0x00) return SC_ERROR_NOT_SUPPORTED; oid[0] = ((id & 0xFF00) >> 8) & 0xFF; oid[1] = id & 0xFF; oid[2] = oid[3] = 0; objectSize = file->size; muscle_parse_acls(file, &read_perm, &write_perm, &delete_perm); r = msc_create_object(card, objectId, objectSize, read_perm, write_perm, delete_perm); mscfs_clear_cache(fs); if(r >= 0) return 0; return r; } static int muscle_create_file(sc_card_t *card, sc_file_t *file) { mscfs_t *fs = MUSCLE_FS(card); size_t objectSize = file->size; unsigned short read_perm = 0, write_perm = 0, delete_perm = 0; msc_id objectId; int r; if(file->type == SC_FILE_TYPE_DF) return muscle_create_directory(card, file); if(file->type != SC_FILE_TYPE_WORKING_EF) return SC_ERROR_NOT_SUPPORTED; if(file->id == 0) /* No null name files */ return SC_ERROR_INVALID_ARGUMENTS; muscle_parse_acls(file, &read_perm, &write_perm, &delete_perm); mscfs_lookup_local(fs, file->id, &objectId); r = msc_create_object(card, objectId, objectSize, read_perm, write_perm, delete_perm); mscfs_clear_cache(fs); if(r >= 0) return 0; return r; } 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; msc_id objectId; u8* oid = objectId.id; mscfs_file_t *file; r = mscfs_check_selection(fs, -1); 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); */ if(!file->ef) { oid[0] = oid[2]; oid[1] = oid[3]; oid[2] = oid[3] = 0; } r = msc_read_object(card, objectId, idx, buf, count); 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) { mscfs_t *fs = MUSCLE_FS(card); int r; mscfs_file_t *file; msc_id objectId; u8* oid = objectId.id; r = mscfs_check_selection(fs, -1); 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); */ if(!file->ef) { oid[0] = oid[2]; oid[1] = oid[3]; oid[2] = oid[3] = 0; } if(file->size < idx + count) { size_t newFileSize = idx + count; u8* buffer = malloc(newFileSize); if(buffer == NULL) LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); r = msc_read_object(card, objectId, 0, buffer, file->size); /* 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; r = msc_create_object(card, objectId, newFileSize, 0,0,0); if(r < 0) goto update_bin_free_buffer; memcpy(buffer + idx, buf, count); r = msc_update_object(card, objectId, 0, buffer, newFileSize); if(r < 0) goto update_bin_free_buffer; file->size = newFileSize; update_bin_free_buffer: free(buffer); LOG_FUNC_RETURN(card->ctx, r); } else { r = msc_update_object(card, objectId, idx, buf, count); } /* mscfs_clear_cache(fs); */ return r; } /* TODO: Evaluate correctness */ static int muscle_delete_mscfs_file(sc_card_t *card, mscfs_file_t *file_data) { mscfs_t *fs = MUSCLE_FS(card); 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 */ r = mscfs_check_cache(fs); if(r < 0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); 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++) { msc_id objectId; childFile = &fs->cache.array[x]; objectId = childFile->objectId; 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]; oid[1] = oid[3]; oid[2] = oid[3] = 0; /* ??? objectId = objectId >> 16; */ } 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 */ if((0 == memcmp(oid, "\x3F\x00\x00\x00", 4)) || (0 == memcmp(oid, "\x3F\x00\x3F\x00", 4))) return 0; if(r < 0) { printf("ID: %02X%02X%02X%02X\n", oid[0],oid[1],oid[2],oid[3]); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,r); } return 0; } static int muscle_delete_file(sc_card_t *card, const sc_path_t *path_in) { mscfs_t *fs = MUSCLE_FS(card); mscfs_file_t *file_data = NULL; int r = 0; 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); return 0; } static void muscle_load_single_acl(sc_file_t* file, int operation, unsigned short acl) { int key; /* Everybody by default.... */ sc_file_add_acl_entry(file, operation, SC_AC_NONE, 0); if(acl == 0xFFFF) { sc_file_add_acl_entry(file, operation, SC_AC_NEVER, 0); return; } for(key = 0; key < 16; key++) { if(acl >> key & 1) { sc_file_add_acl_entry(file, operation, SC_AC_CHV, key); } } } static void muscle_load_file_acls(sc_file_t* file, mscfs_file_t *file_data) { muscle_load_single_acl(file, SC_AC_OP_READ, file_data->read); muscle_load_single_acl(file, SC_AC_OP_WRITE, file_data->write); muscle_load_single_acl(file, SC_AC_OP_UPDATE, file_data->write); muscle_load_single_acl(file, SC_AC_OP_DELETE, file_data->delete); } static void muscle_load_dir_acls(sc_file_t* file, mscfs_file_t *file_data) { muscle_load_single_acl(file, SC_AC_OP_SELECT, 0); muscle_load_single_acl(file, SC_AC_OP_LIST_FILES, 0); muscle_load_single_acl(file, SC_AC_OP_LOCK, 0xFFFF); muscle_load_single_acl(file, SC_AC_OP_DELETE, file_data->delete); muscle_load_single_acl(file, SC_AC_OP_CREATE, file_data->write); } /* Required type = -1 for don't care, 1 for EF, 0 for DF */ static int select_item(sc_card_t *card, const sc_path_t *path_in, sc_file_t ** file_out, int requiredType) { mscfs_t *fs = MUSCLE_FS(card); mscfs_file_t *file_data = NULL; size_t pathlen = path_in->len; int r = 0; int objectIndex; u8* oid; 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) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } oid = file_data->objectId.id; /* Is it a file or directory */ if(file_data->ef) { fs->currentPath[0] = oid[0]; fs->currentPath[1] = oid[1]; 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; fs->currentFile[1] = 0; } fs->currentFileIndex = objectIndex; if(file_out) { sc_file_t *file; file = sc_file_new(); file->path = *path_in; file->size = file_data->size; file->id = (oid[2] << 8) | oid[3]; if(!file_data->ef) { file->type = SC_FILE_TYPE_DF; } else { file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = SC_FILE_EF_TRANSPARENT; } /* Setup ACLS */ if(file_data->ef) { muscle_load_file_acls(file, file_data); } else { muscle_load_dir_acls(file, file_data); /* Setup directory acls... */ } file->magic = SC_FILE_MAGIC; *file_out = file; } return 0; } static int muscle_select_file(sc_card_t *card, const sc_path_t *path_in, sc_file_t **file_out) { int r; assert(card != NULL && path_in != NULL); switch (path_in->type) { case SC_PATH_TYPE_FILE_ID: r = select_item(card, path_in, file_out, 1); break; case SC_PATH_TYPE_DF_NAME: r = select_item(card, path_in, file_out, 0); break; case SC_PATH_TYPE_PATH: r = select_item(card, path_in, file_out, -1); break; default: SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); } if(r > 0) r = 0; SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,r); } static int _listFile(mscfs_file_t *file, int reset, void *udata) { int next = reset ? 0x00 : 0x01; return msc_list_objects( (sc_card_t*)udata, next, file); } 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) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); } memset(card->drv_data, 0, sizeof(muscle_private_t)); priv = MUSCLE_DATA(card); priv->verifiedPins = 0; priv->fs = mscfs_new(); if(!priv->fs) { free(card->drv_data); LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); } priv->fs->udata = card; priv->fs->listFile = _listFile; card->cla = 0xB0; card->flags |= SC_CARD_FLAG_RNG; card->caps |= SC_CARD_CAP_RNG; /* Card type detection */ 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 */ } /* FIXME: Card type detection */ if (1) { unsigned long flags; flags = SC_ALGORITHM_RSA_RAW; flags |= SC_ALGORITHM_RSA_HASH_NONE; flags |= SC_ALGORITHM_ONBOARD_KEY_GEN; _sc_card_add_rsa_alg(card, 1024, flags, 0); _sc_card_add_rsa_alg(card, 2048, flags, 0); } return SC_SUCCESS; } 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, r; int count = 0; 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; 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)) { buf[0] = oid[2]; buf[1] = oid[3]; if(buf[0] == 0x00 && buf[1] == 0x00) continue; /* No directories/null names outside of root */ buf += 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) { muscle_private_t* priv = MUSCLE_DATA(card); const int bufferLength = MSC_MAX_PIN_COMMAND_LENGTH; u8 buffer[MSC_MAX_PIN_COMMAND_LENGTH]; switch(cmd->cmd) { case SC_PIN_CMD_VERIFY: switch(cmd->pin_type) { case SC_AC_CHV: { sc_apdu_t apdu; int r; 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); if(r >= 0) priv->verifiedPins |= (1 << cmd->pin_reference); return r; } case SC_AC_TERM: case SC_AC_PRO: case SC_AC_AUT: case SC_AC_NONE: default: 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; 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); } case SC_AC_TERM: case SC_AC_PRO: case SC_AC_AUT: case SC_AC_NONE: default: 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; 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); } case SC_AC_TERM: case SC_AC_PRO: case SC_AC_AUT: case SC_AC_NONE: default: sc_log(card->ctx, "Unsupported authentication method\n"); return SC_ERROR_NOT_SUPPORTED; } default: sc_log(card->ctx, "Unsupported command\n"); return SC_ERROR_NOT_SUPPORTED; } } static int muscle_card_extract_key(sc_card_t *card, sc_cardctl_muscle_key_info_t *info) { /* CURRENTLY DONT SUPPORT EXTRACTING PRIVATE KEYS... */ switch(info->keyType) { case 1: /* RSA */ return msc_extract_rsa_public_key(card, info->keyLocation, &info->modLength, &info->modValue, &info->expLength, &info->expValue); default: return SC_ERROR_NOT_SUPPORTED; } } static int muscle_card_import_key(sc_card_t *card, sc_cardctl_muscle_key_info_t *info) { /* CURRENTLY DONT SUPPORT EXTRACTING PRIVATE KEYS... */ switch(info->keyType) { case 0x02: /* RSA_PRIVATE */ case 0x03: /* RSA_PRIVATE_CRT */ return msc_import_key(card, info->keyLocation, info); default: return SC_ERROR_NOT_SUPPORTED; } } static int muscle_card_generate_key(sc_card_t *card, sc_cardctl_muscle_gen_key_info_t *info) { return msc_generate_keypair(card, info->privateKeyLocation, info->publicKeyLocation, info->keyType, info->keySize, 0); } static int muscle_card_verified_pins(sc_card_t *card, sc_cardctl_muscle_verified_pins_info_t *info) { muscle_private_t* priv = MUSCLE_DATA(card); info->verifiedPins = priv->verifiedPins; return 0; } static int muscle_card_ctl(sc_card_t *card, unsigned long request, void *data) { switch(request) { case SC_CARDCTL_MUSCLE_GENERATE_KEY: return muscle_card_generate_key(card, (sc_cardctl_muscle_gen_key_info_t*) data); case SC_CARDCTL_MUSCLE_EXTRACT_KEY: return muscle_card_extract_key(card, (sc_cardctl_muscle_key_info_t*) data); case SC_CARDCTL_MUSCLE_IMPORT_KEY: return muscle_card_import_key(card, (sc_cardctl_muscle_key_info_t*) data); case SC_CARDCTL_MUSCLE_VERIFIED_PINS: return muscle_card_verified_pins(card, (sc_cardctl_muscle_verified_pins_info_t*) data); default: return SC_ERROR_NOT_SUPPORTED; /* Unsupported.. whatever it is */ } } static int muscle_set_security_env(sc_card_t *card, const sc_security_env_t *env, int se_num) { muscle_private_t* priv = MUSCLE_DATA(card); if (env->operation != SC_SEC_OPERATION_SIGN && env->operation != SC_SEC_OPERATION_DECIPHER) { sc_log(card->ctx, "Invalid crypto operation supplied.\n"); return SC_ERROR_NOT_SUPPORTED; } if (env->algorithm != SC_ALGORITHM_RSA) { 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_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_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_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_log(card->ctx, "File reference is not 0012.\n"); return SC_ERROR_NOT_SUPPORTED; } */ priv->env = *env; return 0; } static int muscle_restore_security_env(sc_card_t *card, int se_num) { muscle_private_t* priv = MUSCLE_DATA(card); memset(&priv->env, 0, sizeof(priv->env)); return 0; } static int muscle_decipher(sc_card_t * card, const u8 * crgram, size_t crgram_len, u8 * out, size_t out_len) { muscle_private_t* priv = MUSCLE_DATA(card); u8 key_id; int r; /* 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_log(card->ctx, "Output buffer too small"); return SC_ERROR_BUFFER_TOO_SMALL; } r = msc_compute_crypt(card, key_id, 0x00, /* RSA NO PADDING */ 0x04, /* decrypt */ crgram, out, crgram_len, out_len); LOG_TEST_RET(card->ctx, r, "Card signature failed"); return r; } static int muscle_compute_signature(sc_card_t *card, const u8 *data, size_t data_len, u8 * out, size_t outlen) { muscle_private_t* priv = MUSCLE_DATA(card); u8 key_id; int r; key_id = priv->rsa_key_ref * 2; /* Private key */ if (outlen < data_len) { sc_log(card->ctx, "Output buffer too small"); return SC_ERROR_BUFFER_TOO_SMALL; } r = msc_compute_crypt(card, key_id, 0x00, /* RSA NO PADDING */ 0x04, /* -- decrypt raw... will do what we need since signing isn't yet supported */ data, out, data_len, outlen); LOG_TEST_RET(card->ctx, r, "Card signature failed"); return r; } static int muscle_get_challenge(sc_card_t *card, u8 *rnd, size_t len) { if (len == 0) return SC_SUCCESS; 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) { if(sw1 == 0x9C) { switch(sw2) { case 0x01: /* SW_NO_MEMORY_LEFT */ return SC_ERROR_NOT_ENOUGH_MEMORY; case 0x02: /* SW_AUTH_FAILED */ return SC_ERROR_PIN_CODE_INCORRECT; case 0x03: /* SW_OPERATION_NOT_ALLOWED */ return SC_ERROR_NOT_ALLOWED; case 0x05: /* SW_UNSUPPORTED_FEATURE */ return SC_ERROR_NO_CARD_SUPPORT; case 0x06: /* SW_UNAUTHORIZED */ return SC_ERROR_SECURITY_STATUS_NOT_SATISFIED; case 0x07: /* SW_OBJECT_NOT_FOUND */ return SC_ERROR_FILE_NOT_FOUND; case 0x08: /* SW_OBJECT_EXISTS */ return SC_ERROR_FILE_ALREADY_EXISTS; case 0x09: /* SW_INCORRECT_ALG */ return SC_ERROR_INCORRECT_PARAMETERS; case 0x0B: /* SW_SIGNATURE_INVALID */ return SC_ERROR_CARD_CMD_FAILED; case 0x0C: /* SW_IDENTITY_BLOCKED */ return SC_ERROR_AUTH_METHOD_BLOCKED; case 0x0F: /* SW_INVALID_PARAMETER */ case 0x10: /* SW_INCORRECT_P1 */ case 0x11: /* SW_INCORRECT_P2 */ return SC_ERROR_INCORRECT_PARAMETERS; } } 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) { struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); if (iso_ops == NULL) iso_ops = iso_drv->ops; muscle_ops = *iso_drv->ops; muscle_ops.check_sw = muscle_check_sw; muscle_ops.pin_cmd = muscle_pin_cmd; muscle_ops.match_card = muscle_match_card; muscle_ops.init = muscle_init; muscle_ops.finish = muscle_finish; muscle_ops.get_challenge = muscle_get_challenge; muscle_ops.set_security_env = muscle_set_security_env; muscle_ops.restore_security_env = muscle_restore_security_env; muscle_ops.compute_signature = muscle_compute_signature; muscle_ops.decipher = muscle_decipher; muscle_ops.card_ctl = muscle_card_ctl; muscle_ops.read_binary = muscle_read_binary; muscle_ops.update_binary = muscle_update_binary; muscle_ops.create_file = muscle_create_file; 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; } struct sc_card_driver * sc_get_muscle_driver(void) { return sc_get_driver(); } OpenSC-0.26.1/src/libopensc/card-myeid.c000066400000000000000000001676441474147347300177160ustar00rootroot00000000000000/* * card-myeid.c * * 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 * 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 "asn1.h" #include "cardctl.h" #include "types.h" /* 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 /* The following flags define the features supported by the card currently in use. They are used in 'card_supported_features' field in myeid_card_caps struct */ #define MYEID_CARD_CAP_RSA 0x01 #define MYEID_CARD_CAP_3DES 0x02 #define MYEID_CARD_CAP_AES 0x04 #define MYEID_CARD_CAP_ECC 0x08 #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; static struct sc_card_driver myeid_drv = { "MyEID cards with PKCS#15 applet", "myeid", &myeid_ops, NULL, 0, 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 { unsigned char card_caps_ver; unsigned short card_supported_features; unsigned short max_rsa_key_length; unsigned short max_des_key_length; unsigned short max_aes_key_length; unsigned short max_ecc_key_length; } myeid_card_caps_t; static struct myeid_supported_ec_curves { char *curve_name; struct sc_object_id curve_oid; size_t size; } ec_curves[] = { {"secp192r1", {{1, 2, 840, 10045, 3, 1, 1, -1}},192}, /* {"secp224r1", {{1, 3, 132, 0, 33, -1}}, 224}, */ {"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}, }; static int myeid_get_info(struct sc_card *card, u8 *rbuf, size_t buflen); static int myeid_get_card_caps(struct sc_card *card, myeid_card_caps_t* card_caps); static int myeid_match_card(struct sc_card *card) { 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; } static int myeid_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_4_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 > 0) { if (*out_len < apdu.resplen) LOG_TEST_RET(card->ctx, SC_ERROR_BUFFER_TOO_SMALL, "Cannot select AID - response buffer too small."); if (out == NULL) LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "Cannot select AID - invalid arguments."); memcpy(out, apdu.resp, apdu.resplen); *out_len = apdu.resplen; } return SC_SUCCESS; } 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 (!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; 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); } r = SC_SUCCESS; err: return r; } static int myeid_init(struct sc_card *card) { unsigned long flags = 0, ext_flags = 0; myeid_private_data_t *priv; u8 appletInfo[20]; size_t appletInfoLen; myeid_card_caps_t card_caps; size_t resp_len = 0; 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); 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 = myeid_select_aid(card, &myeid_aid, NULL, &resp_len); LOG_TEST_GOTO_ERR(card->ctx, rv, "Failed to select MyEID applet."); /* find out MyEID version */ appletInfoLen = 20; 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; memset(&card_caps, 0, sizeof(myeid_card_caps_t)); card_caps.max_ecc_key_length = 256; card_caps.max_rsa_key_length = 2048; 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. */ 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; ext_flags = SC_ALGORITHM_EXT_EC_NAMEDCURVE | SC_ALGORITHM_EXT_EC_UNCOMPRESES; for (i=0; ec_curves[i].curve_name != NULL; i++) { if (card_caps.max_ecc_key_length >= ec_curves[i].size) _sc_card_add_ec_alg(card, ec_curves[i].size, flags, ext_flags, &ec_curves[i].curve_oid); } } /* show supported symmetric algorithms */ flags = 0; if (card_caps.card_supported_features & MYEID_CARD_CAP_3DES) { 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) _sc_card_add_symmetric_alg(card, SC_ALGORITHM_3DES, 192, flags); } if (card_caps.card_supported_features & MYEID_CARD_CAP_AES) { if (card_caps.max_aes_key_length >= 128) _sc_card_add_symmetric_alg(card, SC_ALGORITHM_AES, 128, flags); if (card_caps.max_aes_key_length >= 256) _sc_card_add_symmetric_alg(card, SC_ALGORITHM_AES, 256, flags); } /* State that we have an RNG */ card->caps |= SC_CARD_CAP_RNG | SC_CARD_CAP_ISO7816_PIN_INFO; 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; 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; case SC_AC_CHV: case SC_AC_TERM: case SC_AC_AUT: if (e->key_ref == SC_AC_KEY_REF_NONE) return 0x00; if (e->key_ref < 1 || e->key_ref > 14) return 0x00; return e->key_ref; case SC_AC_NEVER: return 0x0F; } return 0x00; } static void add_acl_entry(struct sc_file *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(struct sc_file *file, const u8 *buf, size_t len) { int i; const int df_ops[4] = { SC_AC_OP_CREATE, SC_AC_OP_CREATE, SC_AC_OP_DELETE, -1 }; const int ef_ops[4] = { SC_AC_OP_READ, SC_AC_OP_UPDATE, SC_AC_OP_DELETE, -1 }; const int key_ops[4] = { SC_AC_OP_CRYPTO, SC_AC_OP_UPDATE, SC_AC_OP_DELETE, SC_AC_OP_GENERATE }; const int *ops; if (len < 2) 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: ops = key_ops; break; } for (i = 0; i < 4; 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 myeid_select_file(struct sc_card *card, const struct sc_path *in_path, struct sc_file **file) { int r; LOG_FUNC_CALLED(card->ctx); r = iso_ops->select_file(card, in_path, file); if (r == 0 && file != NULL && *file != NULL) parse_sec_attr(*file, (*file)->sec_attr, (*file)->sec_attr_len); 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) { struct sc_apdu apdu; int r; LOG_FUNC_CALLED(card->ctx); sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xCA, 0x01, 0xA1); apdu.resp = buf; apdu.resplen = buflen; apdu.le = buflen > 256 ? 256 : buflen; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.resplen == 0) return sc_check_sw(card, apdu.sw1, apdu.sw2); return (int)apdu.resplen; } static int myeid_process_fci(struct sc_card *card, struct sc_file *file, const u8 *buf, size_t buflen) { myeid_private_data_t *priv = (myeid_private_data_t *) card->drv_data; size_t taglen = 0; const u8 *tag = NULL; int r; LOG_FUNC_CALLED(card->ctx); r = iso_ops->process_fci(card, file, buf, buflen); if (r < 0) LOG_FUNC_RETURN(card->ctx, r); if(file->type == SC_FILE_EF_UNKNOWN) { tag = sc_asn1_find_tag(NULL, buf, buflen, 0x82, &taglen); if (tag != NULL && taglen > 0 && *tag == 17) { file->type = SC_FILE_TYPE_INTERNAL_EF; } } if(file->sec_attr_len >= 3) { 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]); } 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); } static int encode_file_structure(sc_card_t *card, const sc_file_t *file, u8 *buf, size_t *outlen) { const sc_acl_entry_t *read, *update, *delete, *generate; size_t i; LOG_FUNC_CALLED(card->ctx); if (!buf || !outlen || *outlen < 45) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); /* PrivateKey * 0E0000019 6217 81020400 820111 83024B01 8603000000 85028000 8A0100 RESULT 6984 * 6217 81020400 820111 83024B01 8603000000 85021000 8A0100 */ memset(buf, 0x0, *outlen); buf[0] = 0x62; buf[1] = 0x17; /* File size */ buf[2] = (SC_FILE_TYPE_WORKING_EF == file->type ? 0x80 : 0x81); buf[3] = 0x02; buf[4] = (file->size >> 8) & 0xFF; buf[5] = file->size & 0xFF; /* File Description tag */ buf[6] = 0x82; buf[7] = 0x01; buf[8] = 0x01; /* File Identifier tag */ buf[9] = 0x83; buf[10] = 0x02; buf[11] = (file->id >> 8) & 0xFF; buf[12] = file->id & 0xFF; /* Security Attributes Tag */ buf[13] = 0x86; buf[14] = 0x03; buf[15] = 0xFF; buf[16] = 0xFF; buf[17] = 0xFF; if (file->sec_attr_len == 3 && file->sec_attr) { buf[15] = file->sec_attr[0]; buf[16] = file->sec_attr[1]; buf[17] = file->sec_attr[2]; 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]); } else { delete = sc_file_get_acl_entry(file, SC_AC_OP_DELETE); sc_log(card->ctx, "id (%X), type (%X)", file->id, file->type); switch (file->type) { case SC_FILE_TYPE_WORKING_EF: read = sc_file_get_acl_entry(file, SC_AC_OP_READ); update = sc_file_get_acl_entry(file, SC_AC_OP_UPDATE); buf[15] = (acl_to_byte(read) << 4) | acl_to_byte(update); buf[16] = (acl_to_byte(delete)<< 4) | 0x0F; break; case SC_FILE_TYPE_INTERNAL_EF: read = sc_file_get_acl_entry(file, SC_AC_OP_CRYPTO); update = sc_file_get_acl_entry(file, SC_AC_OP_UPDATE); generate = sc_file_get_acl_entry(file, SC_AC_OP_GENERATE); buf[15] = (acl_to_byte(read) << 4) | acl_to_byte(update); buf[16] = (acl_to_byte(delete)<< 4) | acl_to_byte(generate); break; case SC_FILE_TYPE_DF: update = sc_file_get_acl_entry(file, SC_AC_OP_CREATE); buf[15] = (acl_to_byte(update) << 4) | acl_to_byte(update); buf[16] = (acl_to_byte(delete) << 4) | 0x0F; break; default: break; } } /* Proprietary Information */ buf[18] = 0x85; buf[19] = 0x02; 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; buf[23] = 0x01; buf[24] = 0x0; /* RFU */ switch (file->type) { case SC_FILE_TYPE_WORKING_EF: break; case SC_FILE_TYPE_INTERNAL_EF: buf[8] = file->ef_structure; /* RSA or EC */ break; case SC_FILE_TYPE_DF: buf[8] = 0x38; if(file->namelen > 0 && file->namelen <= 16) { buf[25] = 0x84; buf[26] = (u8)file->namelen; for(i=0;i < file->namelen;i++) buf[i + 27] = file->name[i]; buf[1] = 27 + file->namelen; } break; default: sc_log(card->ctx, "Unknown file type\n"); return SC_ERROR_INVALID_ARGUMENTS; } *outlen = buf[1]+2; LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } static int myeid_create_file(struct sc_card *card, struct sc_file *file) { sc_apdu_t apdu; u8 sbuf[45]; size_t buflen = sizeof sbuf; int r; LOG_FUNC_CALLED(card->ctx); r = encode_file_structure(card, file, sbuf, &buflen); if (r) LOG_FUNC_RETURN(card->ctx, 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); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 == 0x6A && apdu.sw2 == 0x89) LOG_FUNC_RETURN(card->ctx, SC_ERROR_FILE_ALREADY_EXISTS); r = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_FUNC_RETURN(card->ctx, r); } static int myeid_delete_file(struct sc_card *card, const struct sc_path *path) { int r; struct sc_apdu apdu; LOG_FUNC_CALLED(card->ctx); if (path->type != SC_PATH_TYPE_FILE_ID && path->len != 2) { sc_log(card->ctx, "File type has to be SC_PATH_TYPE_FILE_ID\n"); LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } r = sc_select_file(card, path, NULL); LOG_TEST_RET(card->ctx, 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); 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_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left) { 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(%zu), pin2 len (%zu)\n", data->pin_reference, data->pin1.len, data->pin2.len); if(data->pin1.len > 8 || data->pin2.len > 8) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_PIN_LENGTH); data->pin1.pad_length = data->pin2.pad_length = 8; data->pin1.pad_char = data->pin2.pad_char = 0xFF; if (data->cmd == SC_PIN_CMD_VERIFY && priv->card_state == SC_FILE_STATUS_CREATION) { sc_log(card->ctx, "Card in creation state, no need to verify"); return SC_SUCCESS; } LOG_FUNC_RETURN(card->ctx, iso_ops->pin_cmd(card, data, tries_left)); } #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) { sc_apdu_t apdu; 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); LOG_FUNC_CALLED(card->ctx); 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_log(card->ctx, "restore security environment not supported.\n"); return SC_ERROR_NOT_SUPPORTED; } sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0, 0); switch (env->operation) { case SC_SEC_OPERATION_DECIPHER: apdu.p1 = 0x41; apdu.p2 = 0xB8; break; case SC_SEC_OPERATION_SIGN: 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; } apdu.le = 0; p = sbuf; if (env->flags & SC_SEC_ENV_ALG_REF_PRESENT) { *p++ = 0x80; /* algorithm reference */ *p++ = 0x01; *p++ = env->algorithm_ref & 0xFF; } if (env->flags & SC_SEC_ENV_FILE_REF_PRESENT) { *p++ = 0x81; *p++ = 2; memcpy(p, env->file_ref.value, 2); p += 2; } /* 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; } 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); 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; } } err: LOG_FUNC_RETURN(card->ctx, r); } static int myeid_set_security_env_ec(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; size_t sz; int r; assert(card != NULL && env != NULL); LOG_FUNC_CALLED(card->ctx); if (env->flags & SC_SEC_ENV_KEY_REF_SYMMETRIC) { sc_log(card->ctx, "symmetric keyref not supported."); return SC_ERROR_NOT_SUPPORTED; } if (se_num > 0) { sc_log(card->ctx, "restore security environment not supported."); return SC_ERROR_NOT_SUPPORTED; } sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0, 0); switch (env->operation) { case SC_SEC_OPERATION_DECIPHER: sc_log(card->ctx, "Decipher operation is not supported with EC keys."); return SC_ERROR_NOT_SUPPORTED; break; case SC_SEC_OPERATION_SIGN: apdu.p1 = 0x41; apdu.p2 = 0xB6; break; case SC_SEC_OPERATION_DERIVE: apdu.p1 = 0x41; apdu.p2 = 0xA4; break; default: return SC_ERROR_INVALID_ARGUMENTS; } apdu.le = 0; p = sbuf; if (env->flags & SC_SEC_ENV_ALG_REF_PRESENT) { *p++ = 0x80; /* algorithm reference */ *p++ = 0x01; *p++ = env->algorithm_ref & 0xFF; } if (env->flags & SC_SEC_ENV_FILE_REF_PRESENT) { *p++ = 0x81; *p++ = 0x02; memcpy(p, env->file_ref.value, 2); p += 2; } if (env->flags & SC_SEC_ENV_KEY_REF_PRESENT) { *p++ = 0x84; *p++ = 1; *p++ = 0; } 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); 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; } } err: LOG_FUNC_RETURN(card->ctx, r); } static int myeid_set_security_env(struct sc_card *card, const struct sc_security_env *env, int se_num) { struct sc_context *ctx = card->ctx; myeid_private_data_t* priv; LOG_FUNC_CALLED(ctx); priv = (myeid_private_data_t*) card->drv_data; /* 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; tmp = *env; tmp.flags &= ~SC_SEC_ENV_ALG_PRESENT; tmp.flags |= SC_SEC_ENV_ALG_REF_PRESENT; if (tmp.algorithm == SC_ALGORITHM_RSA) { 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); } else if (tmp.algorithm == SC_ALGORITHM_EC) { tmp.algorithm_ref = 0x04; 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 { sc_log(ctx, "Unsupported algorithm."); return SC_ERROR_NOT_SUPPORTED; } } return myeid_set_security_env_rsa(card, env, se_num); } static int myeid_convert_ec_signature(struct sc_context *ctx, size_t s_len, unsigned char *data, size_t datalen) { unsigned char *buf; size_t buflen; int r; size_t len_size = 1; size_t sig_len = 0; assert(data && datalen && datalen > 3); /* * When validating the signature data, we have to consider that length of the signature * can be encoded in either one or two bytes depending on key size. With 521 bit keys * length of the structure takes two bytes. */ if (*data != 0x30) return SC_ERROR_INVALID_DATA; if ((*(data + 1) & 0x80) == 0x80) len_size += *(data + 1) & 0x7F; if (len_size == 1) sig_len = *(data + 1); else if (len_size == 2) sig_len = *(data + 2); else if (len_size == 3) { sig_len = *(data + 2) | (*data + 3) << 8; } else return SC_ERROR_INVALID_DATA; if (*(data + 1 + len_size) != 0x02) /* Verify that it is an INTEGER */ if (sig_len != (datalen - len_size - 1)) /* validate size of the DER structure */ return SC_ERROR_INVALID_DATA; /* test&fail early */ buflen = (s_len + 7)/8*2; if (buflen > datalen) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); buf = calloc(1, buflen); if (!buf) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); r = sc_asn1_sig_value_sequence_to_rs(ctx, data, datalen, buf, buflen); if (r < 0) { free(buf); sc_log(ctx, "Failed to convert Sig-Value to the raw RS format"); return r; } memmove(data, buf, buflen); free(buf); 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, u8 * out, size_t outlen) { struct sc_context *ctx; struct sc_apdu apdu; 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); ctx = card->ctx; LOG_FUNC_CALLED(ctx); priv = (myeid_private_data_t*) card->drv_data; 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; memset(sbuf, 0, pad_chars); } } 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; 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); LOG_TEST_RET(ctx, r, "APDU transmit failed"); 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) { 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; } if (apdu.resplen > outlen) LOG_FUNC_RETURN(ctx, SC_ERROR_BUFFER_TOO_SMALL); memcpy(out, apdu.resp, apdu.resplen); LOG_FUNC_RETURN(ctx, (int)apdu.resplen); } /* takes other party's public key as input, performs ECDH key derivation and returns the shared secret in [out]. */ int myeid_ecdh_derive(struct sc_card *card, const u8* pubkey, size_t pubkey_len, u8* out, size_t outlen) { /* MyEID uses GENERAL AUTHENTICATE ISO command for ECDH */ struct sc_apdu apdu; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; int r; size_t ext_len_bytes; sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x86, 0x00, 0x00); apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); /* 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; } memcpy(&sbuf[4 + ext_len_bytes], pubkey, pubkey_len); apdu.lc = pubkey_len + 4 + ext_len_bytes; apdu.le = pubkey_len / 2; apdu.datalen = apdu.lc; apdu.data = sbuf; 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, "ECDH operation failed - GENERAL AUTHENTICATE returned error."); if (outlen < apdu.resplen) { r = SC_ERROR_BUFFER_TOO_SMALL; LOG_TEST_RET(card->ctx, r, "Buffer too small to hold shared secret."); } memcpy(out, rbuf, 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; LOG_FUNC_CALLED(card->ctx); assert(card != NULL && crgram != NULL && out != NULL); 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_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); } r = myeid_transmit_decipher(card, 0x80, 0x86, crgram, crgram_len, out, outlen); LOG_FUNC_RETURN(card->ctx, r); } 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; assert(card != NULL); 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 = sizeof(rbuf) <= 256 ? sizeof(rbuf) : 256; apdu.lc = 0; 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"); if (apdu.resplen <= outlen && out != NULL) memcpy(out, apdu.resp, apdu.resplen); LOG_FUNC_RETURN(ctx, (int)apdu.resplen); } 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; } 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); } /* Write internal data, e.g. add default pin-records to pin */ static int myeid_putdata(struct sc_card *card, struct sc_cardctl_myeid_data_obj* data_obj) { int r; struct sc_apdu apdu; LOG_FUNC_CALLED(card->ctx); memset(&apdu, 0, sizeof(apdu)); apdu.cse = SC_APDU_CASE_3_SHORT; apdu.cla = 0x00; apdu.ins = 0xDA; apdu.p1 = data_obj->P1; apdu.p2 = data_obj->P2; apdu.lc = data_obj->DataLen; apdu.datalen = data_obj->DataLen; apdu.data = data_obj->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, "PUT_DATA returned error"); LOG_FUNC_RETURN(card->ctx, r); } /* Read internal data, e.g. get RSA public key */ static int myeid_getdata(struct sc_card *card, struct sc_cardctl_myeid_data_obj* data_obj) { int r; struct sc_apdu apdu; LOG_FUNC_CALLED(card->ctx); memset(&apdu, 0, sizeof(apdu)); apdu.cse = SC_APDU_CASE_2_SHORT; apdu.cla = 0x00; apdu.ins = 0xCA; /* GET DATA */ apdu.p1 = data_obj->P1; apdu.p2 = data_obj->P2; apdu.lc = 0; apdu.datalen = 0; apdu.data = data_obj->Data; apdu.le = card->max_recv_size; apdu.resp = data_obj->Data; apdu.resplen = data_obj->DataLen; 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_DATA returned error"); if (apdu.resplen > data_obj->DataLen) r = SC_ERROR_WRONG_LENGTH; else data_obj->DataLen = apdu.resplen; LOG_FUNC_RETURN(card->ctx, r); } 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[MYEID_MAX_EXT_APDU_BUFFER_SIZE]; int r; LOG_FUNC_CALLED(card->ctx); if (value_len == 0 || value == NULL) return 0; if (mode == LOAD_KEY_MODULUS && value_len == 256 && !priv->cap_chaining) { 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 = value; apdu.datalen = 128; apdu.lc = 128; 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, "LOAD KEY returned error"); mode = 0x89; value += 128; value_len -= 128; } else if ((mode & 0xff00) == 0 && mode != LOAD_KEY_PUBLIC_EXPONENT && value[0] != 0x00) { /* 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 & 0xFF); apdu.flags = SC_APDU_FLAGS_CHAINING; apdu.cla = 0x00; 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"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_FUNC_RETURN(card->ctx, r); } /* Generate or store a key */ static int myeid_generate_store_key(struct sc_card *card, struct sc_cardctl_myeid_gen_store_key_info *data) { struct sc_apdu apdu; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; int r=0,len; LOG_FUNC_CALLED(card->ctx); /* Setup key-generation parameters */ if (data->op_type == OP_TYPE_GENERATE) { len = 0; memset(&apdu, 0, sizeof(apdu)); if(data->key_type == SC_CARDCTL_MYEID_KEY_RSA) { sbuf[len++] = 0x30; sbuf[len++] = 0x05; sbuf[len++] = 0x81; sbuf[len++] = data->pubexp_len; memcpy(sbuf + len, data->pubexp, data->pubexp_len); len += data->pubexp_len; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x46, 0x00, 0x00); apdu.data = sbuf; } else if(data->key_type == SC_CARDCTL_MYEID_KEY_EC) { sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x46, 0x00, 0x00); apdu.data = NULL; apdu.resp = sbuf; apdu.resplen = 0x00; apdu.le = 0x00; } apdu.cla = 0x00; apdu.datalen = len; apdu.lc = 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); LOG_TEST_RET(card->ctx, r, "GENERATE_KEY returned error"); } else { if(data->key_type == SC_CARDCTL_MYEID_KEY_RSA) { if((r=myeid_loadkey(card, LOAD_KEY_PRIME_P, data->primep, data->primep_len)) >= 0 && (r=myeid_loadkey(card, LOAD_KEY_PRIME_Q, data->primeq, data->primeq_len)) >= 0 && (r=myeid_loadkey(card, LOAD_KEY_DP1, data->dp1, data->dp1_len)) >= 0 && (r=myeid_loadkey(card, LOAD_KEY_DQ1, data->dq1, data->dq1_len)) >= 0 && (r=myeid_loadkey(card, LOAD_KEY_INVQ, data->invq, data->invq_len)) >= 0 && (r=myeid_loadkey(card, LOAD_KEY_MODULUS, data->mod, data->key_len_bits)) >= 0 && (r=myeid_loadkey(card, LOAD_KEY_PUBLIC_EXPONENT, data->pubexp, data->pubexp_len)) >= 0) LOG_FUNC_RETURN(card->ctx, r); } else if(data->key_type == SC_CARDCTL_MYEID_KEY_EC) { if((r = myeid_loadkey(card, LOAD_KEY_EC_PRIVATE, data->d, data->d_len)) >= 0 && (r = myeid_loadkey(card, LOAD_KEY_EC_PUBLIC, data->ecpublic_point, data->ecpublic_point_len)) >= 0) LOG_FUNC_RETURN(card->ctx, r); } else if(data->key_type == SC_CARDCTL_MYEID_KEY_AES || data->key_type == SC_CARDCTL_MYEID_KEY_DES) { if((r = myeid_loadkey(card, LOAD_KEY_SYMMETRIC, data->d, data->d_len)) >= 0) LOG_FUNC_RETURN(card->ctx, r); } } LOG_FUNC_RETURN(card->ctx, r); } static int myeid_activate_card(struct sc_card *card) { int r; u8 sbuf[] ="\xA0\x00\x00\x00\x63\x50\x4B\x43\x53\x2D\x31\x35"; sc_apdu_t apdu; LOG_FUNC_CALLED(card->ctx); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x44, 0x04, 0x00); apdu.cla = 0x00; apdu.data = sbuf; apdu.datalen = 0x0C; apdu.lc = 0x0C; 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, "ACTIVATE_APPLET returned error"); LOG_FUNC_RETURN(card->ctx, r); } static int myeid_get_info(struct sc_card *card, u8 *rbuf, size_t buflen) { sc_apdu_t apdu; int r; LOG_FUNC_CALLED(card->ctx); sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0x01, 0xA0); apdu.resp = rbuf; apdu.resplen = buflen; apdu.le = buflen; 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 != 20) { sc_log(card->ctx, "Unexpected response to GET DATA (applet info)"); return SC_ERROR_INTERNAL; } /* store the applet version */ card->version.fw_major = rbuf[5] * 10 + rbuf[6]; card->version.fw_minor = rbuf[7]; /* add version to name */ snprintf(card_name_buf, sizeof(card_name_buf), "%s %d.%d.%d", card->name, rbuf[5], rbuf[6], rbuf[7]); card->name = card_name_buf; LOG_FUNC_RETURN(card->ctx, r); } static int myeid_get_serialnr(sc_card_t *card, sc_serial_number_t *serial) { int r; u8 rbuf[256]; LOG_FUNC_CALLED(card->ctx); /* if number cached, get it if(card->serialnr.value) { memcpy(serial, &card->serialnr, sizeof(*serial)); LOG_FUNC_RETURN(card->ctx, r); }*/ /* get number from card */ r = myeid_get_info(card, rbuf, sizeof(rbuf)); LOG_TEST_RET(card->ctx, r, "Get applet info failed"); /* cache serial number */ memcpy(card->serialnr.value, &rbuf[8], 10); card->serialnr.len = 10; /* copy and return serial number */ memcpy(serial, &card->serialnr, sizeof(*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. */ static int myeid_get_card_caps(struct sc_card *card, myeid_card_caps_t* card_caps) { sc_apdu_t apdu; int r; unsigned char rbuf[SC_MAX_APDU_BUFFER_SIZE]; LOG_FUNC_CALLED(card->ctx); sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0x01, 0xAA); apdu.resp = rbuf; apdu.resplen = sizeof(myeid_card_caps_t); apdu.le = sizeof(myeid_card_caps_t); 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 < 11) { sc_log(card->ctx, "Unexpected response to GET DATA (MyEIC card capabilities)"); return SC_ERROR_INTERNAL; } card_caps->card_caps_ver = rbuf[0]; /* the card returns big endian values */ card_caps->card_supported_features = (unsigned short) rbuf[1] << 8 | rbuf[2]; card_caps->max_rsa_key_length = (unsigned short) rbuf[3] << 8 | rbuf[4]; card_caps->max_des_key_length = (unsigned short) rbuf[5] << 8 | rbuf[6]; card_caps->max_aes_key_length = (unsigned short) rbuf[7] << 8 | rbuf[8]; card_caps->max_ecc_key_length = (unsigned short) rbuf[9] << 8 | rbuf[10]; LOG_FUNC_RETURN(card->ctx, r); } static int myeid_card_ctl(struct sc_card *card, unsigned long cmd, void *ptr) { int r = SC_ERROR_NOT_SUPPORTED; LOG_FUNC_CALLED(card->ctx); switch(cmd) { case SC_CARDCTL_MYEID_PUTDATA: r = myeid_putdata(card, (struct sc_cardctl_myeid_data_obj*) ptr); break; case SC_CARDCTL_MYEID_GETDATA: r = myeid_getdata(card, (struct sc_cardctl_myeid_data_obj*) ptr); break; case SC_CARDCTL_MYEID_GENERATE_STORE_KEY: r = myeid_generate_store_key(card, (struct sc_cardctl_myeid_gen_store_key_info *) ptr); break; case SC_CARDCTL_MYEID_ACTIVATE_CARD: r = myeid_activate_card(card); break; 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: break; } LOG_FUNC_RETURN(card->ctx, r); } static int myeid_finish(sc_card_t * card) { struct myeid_private_data *priv = (struct myeid_private_data *) card->drv_data; free(priv); 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; assert(card != NULL); 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) { struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); if (iso_ops == NULL) iso_ops = iso_drv->ops; myeid_ops = *iso_drv->ops; myeid_ops.match_card = myeid_match_card; myeid_ops.init = myeid_init; myeid_ops.finish = myeid_finish; /* no record oriented file services */ myeid_ops.read_record = NULL; myeid_ops.write_record = NULL; 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; myeid_ops.set_security_env = myeid_set_security_env; myeid_ops.compute_signature = myeid_compute_signature; myeid_ops.decipher = myeid_decipher; 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; } struct sc_card_driver * sc_get_myeid_driver(void) { return sc_get_driver(); } OpenSC-0.26.1/src/libopensc/card-npa.c000066400000000000000000000535771474147347300173640ustar00rootroot00000000000000/* * card-npa.c: Recognize known German identity cards * * Copyright (C) 2011-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 "card-npa.h" #include "libopensc/internal.h" #include "libopensc/opensc.h" #include "libopensc/pace.h" #include "libopensc/sm.h" #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 { const char *can; unsigned char *st_dv_certificate; size_t st_dv_certificate_len; unsigned char *st_certificate; size_t st_certificate_len; unsigned char *st_key; size_t st_key_len; unsigned char *ef_cardaccess; size_t ef_cardaccess_length; unsigned char *ef_cardsecurity; size_t ef_cardsecurity_length; }; static struct npa_drv_data *npa_drv_data_create(void) { struct npa_drv_data *drv_data = calloc(1, sizeof *drv_data); return drv_data; } static void npa_drv_data_free(struct npa_drv_data *drv_data) { if (drv_data) { free(drv_data->ef_cardaccess); free(drv_data->ef_cardsecurity); free(drv_data->st_certificate); free(drv_data->st_dv_certificate); free(drv_data->st_key); free(drv_data); } } static struct sc_card_operations npa_ops; static struct sc_card_driver npa_drv = { "German ID card (neuer Personalausweis, nPA)", "npa", &npa_ops, NULL, 0, NULL }; static int npa_load_options(sc_context_t *ctx, struct npa_drv_data *drv_data) { int r; size_t i, j; scconf_block **found_blocks, *block; const char *file; if (!ctx || !drv_data) { r = SC_ERROR_INTERNAL; goto err; } for (i = 0; ctx->conf_blocks[i]; i++) { found_blocks = scconf_find_blocks(ctx->conf, ctx->conf_blocks[i], "card_driver", "npa"); if (!found_blocks) continue; for (j = 0, block = found_blocks[j]; block; j++, block = found_blocks[j]) { if (!drv_data->can) drv_data->can = scconf_get_str(block, "can", NULL); if (!drv_data->st_dv_certificate || !drv_data->st_dv_certificate_len) { file = scconf_get_str(block, "st_dv_certificate", NULL); if (!fread_to_eof(file, (unsigned char **) &drv_data->st_dv_certificate, &drv_data->st_dv_certificate_len)) sc_log(ctx, "Warning: Could not read %s.\n", file); } if (!drv_data->st_certificate || !drv_data->st_certificate_len) { file = scconf_get_str(block, "st_certificate", NULL); if (!fread_to_eof(file, (unsigned char **) &drv_data->st_certificate, &drv_data->st_certificate_len)) sc_log(ctx, "Warning: Could not read %s.\n", file); } if (!drv_data->st_key || !drv_data->st_key_len) { file = scconf_get_str(block, "st_key", NULL); if (!fread_to_eof(file, (unsigned char **) &drv_data->st_key, &drv_data->st_key_len)) sc_log(ctx, "Warning: Could not read %s.\n", file); } } free(found_blocks); } r = SC_SUCCESS; err: 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) { 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, struct establish_pace_channel_input *pace_input, struct establish_pace_channel_output *pace_output) { struct npa_drv_data *drv_data; if (card->drv_data) { drv_data = card->drv_data; if (pace_output) { pace_output->ef_cardaccess = drv_data->ef_cardaccess; pace_output->ef_cardaccess_length = drv_data->ef_cardaccess_length; } if (pace_input && pace_input->pin_id == PACE_PIN_ID_CAN) { pace_input->pin = (const unsigned char *) drv_data->can; pace_input->pin_length = drv_data->can ? strlen(drv_data->can) : 0; } } } static void npa_get_cached_ta_params(sc_card_t *card, const unsigned char *certs[2], size_t certs_lens[2], const unsigned char **st_key, size_t *st_key_len) { struct npa_drv_data *drv_data; size_t i; if (card->drv_data) { drv_data = card->drv_data; if (certs && certs_lens) { i = 0; if (drv_data->st_dv_certificate) { certs[i] = drv_data->st_dv_certificate; certs_lens[i] = drv_data->st_dv_certificate_len; i++; } if (drv_data->st_certificate) { certs[i] = drv_data->st_certificate; certs_lens[i] = drv_data->st_certificate_len; } } if (st_key && st_key_len) { *st_key = drv_data->st_key; *st_key_len = drv_data->st_key_len; } } } static void npa_get_cached_ca_params(sc_card_t *card, unsigned char **ef_cardsecurity, size_t *ef_cardsecurity_length) { struct npa_drv_data *drv_data; if (card->drv_data) { drv_data = card->drv_data; if (ef_cardsecurity && ef_cardsecurity_length) { *ef_cardsecurity = drv_data->ef_cardsecurity; *ef_cardsecurity_length = drv_data->ef_cardsecurity_length; } } } static void npa_cache_or_free(sc_card_t *card, unsigned char **ef_cardaccess, size_t *ef_cardaccess_length, unsigned char **ef_cardsecurity, size_t *ef_cardsecurity_length) { struct npa_drv_data *drv_data; if (card && card->drv_data) { drv_data = card->drv_data; if (ef_cardaccess && ef_cardaccess_length && *ef_cardaccess && *ef_cardaccess_length) { drv_data->ef_cardaccess = *ef_cardaccess; drv_data->ef_cardaccess_length = *ef_cardaccess_length; *ef_cardaccess = NULL; *ef_cardaccess_length = 0; } if (ef_cardsecurity && ef_cardsecurity_length && *ef_cardsecurity && *ef_cardsecurity_length) { drv_data->ef_cardsecurity = *ef_cardsecurity; drv_data->ef_cardsecurity_length = *ef_cardsecurity_length; *ef_cardsecurity = NULL; *ef_cardsecurity_length = 0; } } else { if (ef_cardaccess && ef_cardaccess_length) { free(*ef_cardaccess); *ef_cardaccess = NULL; *ef_cardaccess_length = 0; } if (ef_cardsecurity && ef_cardsecurity_length) { free(*ef_cardsecurity); *ef_cardsecurity = NULL; *ef_cardsecurity_length = 0; } } } static int npa_unlock_esign(sc_card_t *card) { int r = SC_ERROR_INTERNAL; struct establish_pace_channel_input pace_input; struct establish_pace_channel_output pace_output; const unsigned char *certs[] = { NULL, NULL }; size_t certs_lens[] = { 0, 0}; const unsigned char *st_key = NULL; size_t st_key_len = 0; unsigned char *ef_cardsecurity = NULL; size_t ef_cardsecurity_len = 0; memset(&pace_input, 0, sizeof pace_input); memset(&pace_output, 0, sizeof pace_output); if (!card) { r = SC_ERROR_INVALID_CARD; goto err; } sc_log(card->ctx, "Will verify CAN first for unlocking eSign application.\n"); pace_input.chat = esign_chat; pace_input.chat_length = sizeof esign_chat; pace_input.pin_id = PACE_PIN_ID_CAN; npa_get_cached_pace_params(card, &pace_input, &pace_output); npa_get_cached_ta_params(card, certs, certs_lens, &st_key, &st_key_len); npa_get_cached_ca_params(card, &ef_cardsecurity, &ef_cardsecurity_len); if (!(card->reader && (card->reader->capabilities & SC_READER_CAP_PACE_ESIGN)) && (!st_key || !st_key_len)) { sc_log(card->ctx, "QES requires a comfort reader (CAT-K) or a ST certificate.\n"); r = SC_ERROR_NOT_SUPPORTED; goto err; } /* FIXME set flags with opensc.conf */ 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 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"); goto err; } if (card->reader->capabilities & SC_READER_CAP_PACE_ESIGN) { sc_log(card->ctx, "Proved Access rights to eSign application with comfort reader (CAT-K).\n"); } else { r = perform_terminal_authentication(card, certs, certs_lens, st_key, st_key_len, NULL, 0); if (r != SC_SUCCESS) { sc_log(card->ctx, "Error authenticating as signature terminal.\n"); goto err; } r = perform_chip_authentication(card, &ef_cardsecurity, &ef_cardsecurity_len); if ( SC_SUCCESS != r) { 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"); } err: npa_cache_or_free(card, &pace_output.ef_cardaccess, &pace_output.ef_cardaccess_length, &ef_cardsecurity, &ef_cardsecurity_len); free(pace_output.recent_car); free(pace_output.previous_car); free(pace_output.id_icc); free(pace_output.id_pcd); 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; int ext_flags = 0; int r; if (!card) { r = SC_ERROR_INVALID_CARD; goto err; } card->caps |= SC_CARD_CAP_APDU_EXT | SC_CARD_CAP_RNG; /* 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; #ifdef ENABLE_SM memset(&card->sm_ctx, 0, sizeof card->sm_ctx); #endif r = _sc_card_add_ec_alg(card, 192, flags, ext_flags, NULL); if (r != SC_SUCCESS) goto err; r = _sc_card_add_ec_alg(card, 224, flags, ext_flags, NULL); if (r != SC_SUCCESS) goto err; r = _sc_card_add_ec_alg(card, 256, flags, ext_flags, NULL); if (r != SC_SUCCESS) goto err; /* nPA does not encode the proprietary fieldSize in PrivateECKeyAttributes, * which leaves it at 0 for OpenSC, so we need to add 0x00 as supported * field_length */ r = _sc_card_add_ec_alg(card, 0, flags, ext_flags, NULL); if (r != SC_SUCCESS) goto err; card->drv_data = npa_drv_data_create(); if (!card->drv_data) { npa_finish(card); r = SC_ERROR_OUT_OF_MEMORY; goto err; } r = npa_load_options(card->ctx, card->drv_data); 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, "Probably not all functionality will be available.\n"); } err: return r; } static int npa_set_security_env(struct sc_card *card, const struct sc_security_env *env, int se_num) { int r; struct sc_card_driver *iso_drv; struct sc_security_env fixed_env; iso_drv = sc_get_iso7816_driver(); if (!env || !iso_drv || !iso_drv->ops || !iso_drv->ops->set_security_env) { r = SC_ERROR_INTERNAL; } else { memcpy(&fixed_env, env, sizeof fixed_env); if (env->operation == SC_SEC_OPERATION_SIGN) { /* The pkcs#15 layer assumes that the field_size of the private key * object is correctly initialized and wants to include it as * algorithm reference. We disable it here */ fixed_env.flags &= ~SC_SEC_ENV_ALG_REF_PRESENT; } r = iso_drv->ops->set_security_env(card, &fixed_env, se_num); } return r; } static int npa_pin_cmd_get_info(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_left) { int r; u8 pin_reference; if (!data || data->pin_type != SC_AC_CHV || !tries_left) { r = SC_ERROR_INVALID_ARGUMENTS; goto err; } pin_reference = data->pin_reference; switch (data->pin_reference) { 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; r = SC_SUCCESS; break; case PACE_PIN_ID_PUK: /* usually 10 tries */ *tries_left = 10; data->pin1.max_tries = 10; r = eac_pace_get_tries_left(card, pin_reference, tries_left); data->pin1.tries_left = *tries_left; break; case PACE_PIN_ID_PIN: /* usually 3 tries */ *tries_left = 3; data->pin1.max_tries = 3; r = eac_pace_get_tries_left(card, pin_reference, tries_left); data->pin1.tries_left = *tries_left; break; default: r = SC_ERROR_OBJECT_NOT_FOUND; goto err; } err: return r; } 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) { int r; 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 (chat) { pace_input.chat = chat; pace_input.chat_length = chat_length; } pace_input.pin_id = pin_reference; if (pin) { pace_input.pin = pin->data; pace_input.pin_length = pin->len; } npa_get_cached_pace_params(card, &pace_input, &pace_output); 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; } } /* resume the PIN if needed */ if (pin_reference == PACE_PIN_ID_PIN && 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) <= 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", eac_secret_name(pin_reference), eac_secret_name(PACE_PIN_ID_CAN)); pace_input.pin_id = PACE_PIN_ID_CAN; pace_input.pin = NULL; pace_input.pin_length = 0; r = perform_pace(card, pace_input, &pace_output, EAC_TR_VERSION_2_02); if (r == SC_SUCCESS) { pace_input.pin_id = pin_reference; if (pin) { pace_input.pin = pin->data; pace_input.pin_length = pin->len; } r = perform_pace(card, pace_input, &pace_output, EAC_TR_VERSION_2_02); if (r == SC_SUCCESS) { sc_log(card->ctx, "%s resumed.\n", eac_secret_name(pin_reference)); if (tries_left) { *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 (pin_reference == PACE_PIN_ID_PIN && tries_left) { if (*tries_left == 0) { sc_log(card->ctx, "%s is suspended and must be resumed.\n", eac_secret_name(pin_reference)); } else if (*tries_left == 1) { sc_log(card->ctx, "%s is blocked and must be unblocked.\n", eac_secret_name(pin_reference)); } } npa_cache_or_free(card, &pace_output.ef_cardaccess, &pace_output.ef_cardaccess_length, NULL, NULL); free(pace_output.recent_car); free(pace_output.previous_car); free(pace_output.id_icc); free(pace_output.id_pcd); return r; } static int npa_standard_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_left) { int r; struct sc_card_driver *iso_drv; iso_drv = sc_get_iso7816_driver(); 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); } 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) { int r; if (!data) { r = SC_ERROR_INVALID_ARGUMENTS; goto err; } if (data->pin_type != SC_AC_CHV) { r = SC_ERROR_NOT_SUPPORTED; goto err; } switch (data->cmd) { case SC_PIN_CMD_GET_INFO: r = npa_pin_cmd_get_info(card, data, tries_left); if (r != SC_SUCCESS) goto err; break; case SC_PIN_CMD_UNBLOCK: #ifdef ENABLE_SM /* opensc-explorer unblocks the PIN by only sending * SC_PIN_CMD_UNBLOCK whereas the PKCS#15 framework first verifies * the PUK with SC_PIN_CMD_VERIFY and then calls with * SC_PIN_CMD_UNBLOCK. * * Here we determine whether the PUK has been verified or not by * checking if an SM channel has been established. */ 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); if (r != SC_SUCCESS) goto err; } #endif r = npa_reset_retry_counter(card, data->pin_reference, 0, NULL, 0); if (r != SC_SUCCESS) goto err; break; case SC_PIN_CMD_CHANGE: case SC_PIN_CMD_VERIFY: switch (data->pin_reference) { case PACE_PIN_ID_CAN: case PACE_PIN_ID_PUK: case PACE_PIN_ID_MRZ: case PACE_PIN_ID_PIN: r = npa_pace_verify(card, data->pin_reference, &(data->pin1), NULL, 0, tries_left); if (r != SC_SUCCESS) goto err; break; default: /* assuming QES PIN */ /* We assume that the eSign application has already been * unlocked, see npa_init(). * * Now, verify the QES PIN. */ r = npa_standard_pin_cmd(card, data, tries_left); if (r != SC_SUCCESS) goto err; break; } if (data->cmd == SC_PIN_CMD_CHANGE) { r = npa_reset_retry_counter(card, data->pin_reference, 1, (const char *) data->pin2.data, data->pin2.len); if (r != SC_SUCCESS) goto err; } break; default: r = SC_ERROR_INTERNAL; goto err; break; } err: LOG_FUNC_RETURN(card->ctx, r); } static int npa_logout(sc_card_t *card) { struct sc_apdu apdu; sc_sm_stop(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 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; if (SC_SUCCESS != sc_transmit_apdu(card, &apdu)) sc_log(card->ctx, "Warning: Could not logout."); } return sc_select_file(card, sc_get_mf_path(), NULL); } struct sc_card_driver *sc_get_npa_driver(void) { struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); npa_ops = *iso_drv->ops; npa_ops.match_card = npa_match_card; npa_ops.init = npa_init; npa_ops.finish = npa_finish; npa_ops.set_security_env = npa_set_security_env; npa_ops.pin_cmd = npa_pin_cmd; npa_ops.logout = npa_logout; return &npa_drv; } OpenSC-0.26.1/src/libopensc/card-npa.h000066400000000000000000000047531474147347300173610ustar00rootroot00000000000000/* * Copyright (C) 2014-2015 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 _CARD_NPA_H #define _CARD_NPA_H #ifdef __cplusplus extern "C" { #endif #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, }; 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 } #endif #endif OpenSC-0.26.1/src/libopensc/card-nqApplet.c000066400000000000000000000372331474147347300203610ustar00rootroot00000000000000/* * 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}, {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; sc_apdu_t apdu; 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); sc_format_apdu_ex(&apdu, 0x00, 0xA4, 0x04, 0x00, nqapplet_aid, cb_aid, buffer, cb_buffer); rv = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(ctx, rv, "APDU transmit failure."); rv = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(card->ctx, rv, "Card returned error"); if (apdu.resplen < 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(card->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; 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"); if (apdu.resplen == 0) { LOG_FUNC_RETURN(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2)); } *cb_resp = apdu.resplen; 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; } 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; /* 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.pin_cmd; nqapplet_operations.card_reader_lock_obtained; nqapplet_operations.wrap; nqapplet_operations.unwrap; */ return &nqapplet_driver; } OpenSC-0.26.1/src/libopensc/card-oberthur.c000066400000000000000000002033211474147347300204200ustar00rootroot00000000000000/* * card-oberthur.c: Support for Oberthur smart cards * CosmopolIC v5; * * Copyright (C) 2001, 2002 Juha Yrjölä * Copyright (C) 2009 Viktor Tarasov , * OpenTrust * * 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 * * best view with tabstop=4 */ #ifdef 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" #include "gp.h" #define OBERTHUR_PIN_LOCAL 0x80 #define OBERTHUR_PIN_REFERENCE_USER 0x81 #define OBERTHUR_PIN_REFERENCE_ONETIME 0x82 #define OBERTHUR_PIN_REFERENCE_SO 0x04 #define OBERTHUR_PIN_REFERENCE_PUK 0x84 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, "Oberthur 64k v4/2.1.1", SC_CARD_TYPE_OBERTHUR_64K, 0, NULL }, { "3B:7D:11:00:00:00:31:80:71:8E:64:77:E3:01:00:82:90:00", NULL, "Oberthur 64k v5", SC_CARD_TYPE_OBERTHUR_64K, 0, NULL }, { "3B:7D:11:00:00:00:31:80:71:8E:64:77:E3:02:00:82:90:00", NULL, "Oberthur 64k v5/2.2.0", SC_CARD_TYPE_OBERTHUR_64K, 0, NULL }, { "3B:7B:18:00:00:00:31:C0:64:77:E3:03:00:82:90:00", NULL, "Oberthur 64k CosmopolIC v5.2/2.2", SC_CARD_TYPE_OBERTHUR_64K, 0, NULL }, { "3B:FB:11:00:00:81:31:FE:45:00:31:C0:64:77:E9:10:00:00:90:00:6A", NULL, "OCS ID-One Cosmo Card", SC_CARD_TYPE_OBERTHUR_64K, 0, NULL }, { NULL, NULL, NULL, 0, 0, NULL } }; struct auth_senv { unsigned long algorithm; int key_file_id; size_t key_size; }; struct auth_private_data { unsigned char aid[SC_MAX_AID_SIZE]; int aid_len; struct sc_pin_cmd_pin pin_info; struct auth_senv senv; long int sn; }; struct auth_update_component_info { enum SC_CARDCTL_OBERTHUR_KEY_TYPE type; unsigned int component; unsigned char *data; size_t len; }; static const unsigned char *aidAuthentIC_V5 = (const unsigned char *)"\xA0\x00\x00\x00\x77\x01\x03\x03\x00\x00\x00\xF1\x00\x00\x00\x02"; static const int lenAidAuthentIC_V5 = 16; static const char *nameAidAuthentIC_V5 = "AuthentIC v5"; #define OBERTHUR_AUTH_TYPE_PIN 1 #define OBERTHUR_AUTH_TYPE_PUK 2 #define OBERTHUR_AUTH_MAX_LENGTH_PIN 64 #define OBERTHUR_AUTH_MAX_LENGTH_PUK 16 #define SC_OBERTHUR_MAX_ATTR_SIZE 8 #define PUBKEY_512_ASN1_SIZE 0x4A #define PUBKEY_1024_ASN1_SIZE 0x8C #define PUBKEY_2048_ASN1_SIZE 0x10E static unsigned char rsa_der[PUBKEY_2048_ASN1_SIZE]; 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; static struct sc_card_operations *iso_ops; static struct sc_card_driver auth_drv = { "Oberthur AuthentIC.v2/CosmopolIC.v4", "oberthur", &auth_ops, NULL, 0, NULL }; static int auth_get_pin_reference (struct sc_card *card, int type, int reference, int cmd, int *out_ref); static int auth_read_component(struct sc_card *card, enum SC_CARDCTL_OBERTHUR_KEY_TYPE type, int num, unsigned char *out, size_t outlen); 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); static int auth_pin_reset(struct sc_card *card, unsigned int type, struct sc_pin_cmd_data *data, int *tries_left); 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); static int auth_select_file(struct sc_card *card, const struct sc_path *in_path, struct sc_file **file_out); static int acl_to_ac_byte(struct sc_card *card, const struct sc_acl_entry *e); static int auth_finish(struct sc_card *card) { free(card->drv_data); return SC_SUCCESS; } static int 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; int rv, ii; struct sc_path tmp_path; /* Select Card Manager (to deselect previously selected application) */ rv = gp_select_card_manager(card); LOG_TEST_RET(card->ctx, rv, "APDU transmit failed"); /* Get smart card serial number */ sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xCA, 0x9F, 0x7F); apdu.cla = 0x80; apdu.le = 0x2D; apdu.resplen = 0x30; apdu.resp = apdu_resp; 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); for (ii=0, data->sn = 0; ii < 4; ii++) data->sn += (long int)(*(apdu.resp + 15 + ii)) << (3-ii)*8; sc_log(card->ctx, "serial number %li/0x%lX", data->sn, data->sn); memset(&tmp_path, 0, sizeof(struct sc_path)); tmp_path.type = SC_PATH_TYPE_DF_NAME; memcpy(tmp_path.value, aidAuthentIC_V5, lenAidAuthentIC_V5); tmp_path.len = lenAidAuthentIC_V5; rv = iso_ops->select_file(card, &tmp_path, NULL); 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); data->aid_len = lenAidAuthentIC_V5; card->name = nameAidAuthentIC_V5; LOG_FUNC_RETURN(card->ctx, rv); } static int auth_match_card(struct sc_card *card) { if (_sc_match_atr(card, oberthur_atrs, &card->type) < 0) return 0; else return 1; } static int auth_init(struct sc_card *card) { struct auth_private_data *data; struct sc_path path; unsigned long flags; int rv = 0; data = calloc(1, sizeof(struct auth_private_data)); if (!data) LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); card->cla = 0x00; card->drv_data = data; card->caps |= SC_CARD_CAP_RNG; card->caps |= SC_CARD_CAP_USE_FCI_AC; if (auth_select_aid(card)) { sc_log(card->ctx, "Failed to initialize %s", card->name); rv = SC_ERROR_INVALID_CARD; LOG_TEST_GOTO_ERR(card->ctx, SC_ERROR_INVALID_CARD, "Failed to initialize"); } 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); } 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) { 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; } switch (acl_byte) { case 0x00: sc_file_add_acl_entry(file, op, SC_AC_NONE, SC_AC_KEY_REF_NONE); break; /* User and OneTime PINs are locals */ case 0x21: case 0x22: sc_file_add_acl_entry(file, op, SC_AC_CHV, (acl_byte & 0x0F) | OBERTHUR_PIN_LOCAL); break; /* Local SOPIN is only for the unblocking. */ case 0x24: case 0x25: if (op == SC_AC_OP_PIN_RESET) sc_file_add_acl_entry(file, op, SC_AC_CHV, 0x84); else sc_file_add_acl_entry(file, op, SC_AC_CHV, 0x04); break; case 0xFF: sc_file_add_acl_entry(file, op, SC_AC_NEVER, SC_AC_KEY_REF_NONE); break; default: sc_file_add_acl_entry(file, op, SC_AC_UNKNOWN, SC_AC_KEY_REF_NONE); break; } } static int auth_process_fci(struct sc_card *card, struct sc_file *file, const unsigned char *buf, size_t buflen) { unsigned char type; const unsigned char *attr; size_t attr_len = 0; LOG_FUNC_CALLED(card->ctx); 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 = 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 = 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 = 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; file->size = attr[0]*0x100 + attr[1]; if (file->size==512) file->size = PUBKEY_512_ASN1_SIZE; else if (file->size==1024) file->size = PUBKEY_1024_ASN1_SIZE; else if (file->size==2048) file->size = PUBKEY_2048_ASN1_SIZE; else { sc_log(card->ctx, "Not supported public key size: %"SC_FORMAT_LEN_SIZE_T"u", file->size); LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); } 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)) LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); break; default: LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); } 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) { add_acl_entry(card, file, SC_AC_OP_CREATE, attr[0]); add_acl_entry(card, file, SC_AC_OP_CRYPTO, attr[1]); add_acl_entry(card, file, SC_AC_OP_LIST_FILES, attr[2]); add_acl_entry(card, file, SC_AC_OP_DELETE, attr[3]); add_acl_entry(card, file, SC_AC_OP_PIN_DEFINE, attr[4]); 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 */ switch (file->ef_structure) { case SC_CARDCTL_OBERTHUR_KEY_DES: add_acl_entry(card, file, SC_AC_OP_UPDATE, attr[0]); add_acl_entry(card, file, SC_AC_OP_PSO_DECRYPT, attr[1]); add_acl_entry(card, file, SC_AC_OP_PSO_ENCRYPT, attr[2]); add_acl_entry(card, file, SC_AC_OP_PSO_COMPUTE_CHECKSUM, attr[3]); add_acl_entry(card, file, SC_AC_OP_PSO_VERIFY_CHECKSUM, attr[4]); add_acl_entry(card, file, SC_AC_OP_INTERNAL_AUTHENTICATE, attr[5]); add_acl_entry(card, file, SC_AC_OP_EXTERNAL_AUTHENTICATE, attr[6]); break; case SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC: add_acl_entry(card, file, SC_AC_OP_UPDATE, attr[0]); add_acl_entry(card, file, SC_AC_OP_PSO_ENCRYPT, attr[2]); add_acl_entry(card, file, SC_AC_OP_PSO_VERIFY_SIGNATURE, attr[4]); add_acl_entry(card, file, SC_AC_OP_EXTERNAL_AUTHENTICATE, attr[6]); break; case SC_CARDCTL_OBERTHUR_KEY_RSA_CRT: add_acl_entry(card, file, SC_AC_OP_UPDATE, attr[0]); add_acl_entry(card, file, SC_AC_OP_PSO_DECRYPT, attr[1]); add_acl_entry(card, file, SC_AC_OP_PSO_COMPUTE_SIGNATURE, attr[3]); add_acl_entry(card, file, SC_AC_OP_INTERNAL_AUTHENTICATE, attr[5]); break; } } else { switch (file->ef_structure) { case SC_FILE_EF_TRANSPARENT: add_acl_entry(card, file, SC_AC_OP_WRITE, attr[0]); add_acl_entry(card, file, SC_AC_OP_UPDATE, attr[1]); add_acl_entry(card, file, SC_AC_OP_READ, attr[2]); add_acl_entry(card, file, SC_AC_OP_ERASE, attr[3]); break; case SC_FILE_EF_LINEAR_VARIABLE: add_acl_entry(card, file, SC_AC_OP_WRITE, attr[0]); add_acl_entry(card, file, SC_AC_OP_UPDATE, attr[1]); add_acl_entry(card, file, SC_AC_OP_READ, attr[2]); add_acl_entry(card, file, SC_AC_OP_ERASE, attr[3]); break; } } file->status = SC_FILE_STATUS_ACTIVATED; file->magic = SC_FILE_MAGIC; LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } static int auth_select_file(struct sc_card *card, const struct sc_path *in_path, struct sc_file **file_out) { struct sc_path path; struct sc_file *tmp_file = NULL; size_t offs, ii; int rv; 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", auth_current_df->path.type, sc_print_path(&auth_current_df->path)); if (auth_current_ef) 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) { sc_file_free(auth_current_ef); auth_current_ef = NULL; rv = iso_ops->select_file(card, &path, &tmp_file); LOG_TEST_RET(card->ctx, rv, "select file failed"); if (!tmp_file) return SC_ERROR_OBJECT_NOT_FOUND; 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) { 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 { 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) { 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) { rv = iso_ops->select_file(card, &path, NULL); if (rv) { sc_file_free(auth_current_ef); auth_current_ef = NULL; } LOG_TEST_RET(card->ctx, rv, "select file failed"); } 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) { 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) { 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) { 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) && file_out) { sc_file_free(*file_out); sc_file_dup(file_out, auth_current_df); } else if (auth_current_ef && file_out) { sc_file_free(*file_out); sc_file_dup(file_out, auth_current_ef); } else { LOG_TEST_RET(card->ctx, SC_ERROR_INTERNAL, "No current EF"); } } } LOG_FUNC_RETURN(card->ctx, 0); } static int auth_list_files(struct sc_card *card, unsigned char *buf, size_t buflen) { struct sc_apdu apdu; unsigned char rbuf[SC_MAX_APDU_BUFFER_SIZE]; int rv; LOG_FUNC_CALLED(card->ctx); sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x34, 0, 0); apdu.cla = 0x80; apdu.le = 0x40; apdu.resplen = sizeof(rbuf); apdu.resp = rbuf; 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, "Card returned error"); if (apdu.resplen == 0x100 && rbuf[0]==0 && rbuf[1]==0) LOG_FUNC_RETURN(card->ctx, 0); buflen = buflen < apdu.resplen ? buflen : apdu.resplen; memcpy(buf, rbuf, buflen); LOG_FUNC_RETURN(card->ctx, (int)buflen); } static int auth_delete_file(struct sc_card *card, const struct sc_path *path) { struct sc_apdu apdu; unsigned char sbuf[2]; int rv; char pbuf[SC_MAX_PATH_STRING_SIZE]; LOG_FUNC_CALLED(card->ctx); rv = sc_path_print(pbuf, sizeof(pbuf), path); if (rv != SC_SUCCESS) pbuf[0] = '\0'; sc_log(card->ctx, "path; type=%d, path=%s", path->type, pbuf); if (path->len < 2) { sc_log(card->ctx, "Invalid path length"); LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } if (path->len > 2) { struct sc_path parent = *path; parent.len -= 2; parent.type = SC_PATH_TYPE_PATH; rv = auth_select_file(card, &parent, NULL); LOG_TEST_RET(card->ctx, rv, "select parent failed "); } sbuf[0] = path->value[path->len - 2]; sbuf[1] = path->value[path->len - 1]; if (memcmp(sbuf,"\x00\x00",2)==0 || (memcmp(sbuf,"\xFF\xFF",2)==0) || memcmp(sbuf,"\x3F\xFF",2)==0) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INCORRECT_PARAMETERS); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE4, 0x02, 0x00); apdu.lc = 2; apdu.datalen = 2; apdu.data = sbuf; rv = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, rv, "APDU transmit failed"); if (apdu.sw1==0x6A && apdu.sw2==0x82) { /* Clean up tDF contents.*/ struct sc_path tmp_path; int ii, len; unsigned char lbuf[SC_MAX_APDU_BUFFER_SIZE]; memset(&tmp_path, 0, sizeof(struct sc_path)); tmp_path.type = SC_PATH_TYPE_FILE_ID; memcpy(tmp_path.value, sbuf, 2); tmp_path.len = 2; rv = auth_select_file(card, &tmp_path, NULL); LOG_TEST_RET(card->ctx, rv, "select DF failed"); len = auth_list_files(card, lbuf, sizeof(lbuf)); LOG_TEST_RET(card->ctx, len, "list DF failed"); for (ii=0; iictx, rv, "delete failed"); } tmp_path.type = SC_PATH_TYPE_PARENT; rv = auth_select_file(card, &tmp_path, NULL); LOG_TEST_RET(card->ctx, rv, "select parent failed"); apdu.p1 = 1; 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_FUNC_RETURN(card->ctx, rv); } static int acl_to_ac_byte(struct sc_card *card, const struct sc_acl_entry *e) { unsigned key_ref; if (e == NULL) return SC_ERROR_OBJECT_NOT_FOUND; key_ref = e->key_ref & ~OBERTHUR_PIN_LOCAL; switch (e->method) { case SC_AC_NONE: LOG_FUNC_RETURN(card->ctx, 0); case SC_AC_CHV: if (key_ref > 0 && key_ref < 6) LOG_FUNC_RETURN(card->ctx, (0x20 | key_ref)); else LOG_FUNC_RETURN(card->ctx, SC_ERROR_INCORRECT_PARAMETERS); case SC_AC_PRO: if (((key_ref & 0xE0) != 0x60) || ((key_ref & 0x18) == 0)) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INCORRECT_PARAMETERS); else LOG_FUNC_RETURN(card->ctx, key_ref); case SC_AC_NEVER: return 0xff; } LOG_FUNC_RETURN(card->ctx, SC_ERROR_INCORRECT_PARAMETERS); } static int encode_file_structure_V5(struct sc_card *card, const struct sc_file *file, unsigned char *buf, size_t *buflen) { size_t ii; int rv = 0; size_t size; unsigned char *p = buf; unsigned char ops[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; LOG_FUNC_CALLED(card->ctx); sc_log(card->ctx, "id %04X; size %"SC_FORMAT_LEN_SIZE_T"u; type 0x%X/0x%X", file->id, file->size, file->type, file->ef_structure); if (*buflen < 0x18) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INCORRECT_PARAMETERS); p[0] = 0x62, p[1] = 0x16; p[2] = 0x82, p[3] = 0x02; rv = 0; if (file->type == SC_FILE_TYPE_DF) { p[4] = 0x38; p[5] = 0x00; } else if (file->type == SC_FILE_TYPE_WORKING_EF) { switch (file->ef_structure) { case SC_FILE_EF_TRANSPARENT: p[4] = 0x01; p[5] = 0x01; break; case SC_FILE_EF_LINEAR_VARIABLE: p[4] = 0x04; p[5] = 0x01; break; default: rv = SC_ERROR_INVALID_ARGUMENTS; break; } } else if (file->type == SC_FILE_TYPE_INTERNAL_EF) { switch (file->ef_structure) { case SC_CARDCTL_OBERTHUR_KEY_DES: p[4] = 0x11; p[5] = 0x00; break; case SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC: p[4] = 0x12; p[5] = 0x00; break; case SC_CARDCTL_OBERTHUR_KEY_RSA_CRT: p[4] = 0x14; p[5] = 0x00; break; default: rv = -1; break; } } else rv = SC_ERROR_INVALID_ARGUMENTS; 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); } p[6] = 0x83; p[7] = 0x02; p[8] = file->id >> 8; p[9] = file->id & 0xFF; p[10] = 0x85; p[11] = 0x02; size = file->size; 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) { sc_log(card->ctx, "ef %s","SC_FILE_EF_RSA_PUBLIC"); if (file->size == PUBKEY_512_ASN1_SIZE || file->size == 512) size = 512; else if (file->size == PUBKEY_1024_ASN1_SIZE || file->size == 1024) size = 1024; else if (file->size == PUBKEY_2048_ASN1_SIZE || file->size == 2048) size = 2048; 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) { 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 { sc_log(card->ctx, "incorrect DES size %"SC_FORMAT_LEN_SIZE_T"u", file->size); LOG_FUNC_RETURN(card->ctx, SC_ERROR_INCORRECT_PARAMETERS); } } p[12] = (size >> 8) & 0xFF; p[13] = size & 0xFF; p[14] = 0x86; p[15] = 0x08; if (file->type == SC_FILE_TYPE_DF) { ops[0] = SC_AC_OP_CREATE; ops[1] = SC_AC_OP_CRYPTO; ops[2] = SC_AC_OP_LIST_FILES; ops[3] = SC_AC_OP_DELETE; 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) { 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) { 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) { sc_log(card->ctx, "EF_DES"); ops[0] = SC_AC_OP_UPDATE; ops[1] = SC_AC_OP_PSO_DECRYPT; ops[2] = SC_AC_OP_PSO_ENCRYPT; ops[3] = SC_AC_OP_PSO_COMPUTE_CHECKSUM; 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) { 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) { sc_log(card->ctx, "EF_RSA_PRIVATE"); ops[0] = SC_AC_OP_UPDATE; ops[1] = SC_AC_OP_PSO_DECRYPT; ops[3] = SC_AC_OP_PSO_COMPUTE_SIGNATURE; ops[5] = SC_AC_OP_INTERNAL_AUTHENTICATE; } } for (ii = 0; ii < sizeof(ops); ii++) { const struct sc_acl_entry *entry; p[16+ii] = 0xFF; if (ops[ii]==0xFF) continue; entry = sc_file_get_acl_entry(file, ops[ii]); rv = acl_to_ac_byte(card,entry); LOG_TEST_RET(card->ctx, rv, "Invalid ACL"); p[16+ii] = rv; } *buflen = 0x18; LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } static int auth_create_file(struct sc_card *card, struct sc_file *file) { struct sc_apdu apdu; struct sc_path path; int rv, rec_nr; unsigned char sbuf[0x18]; size_t sendlen = sizeof(sbuf); char pbuf[SC_MAX_PATH_STRING_SIZE]; LOG_FUNC_CALLED(card->ctx); rv = sc_path_print(pbuf, sizeof(pbuf), &file->path); if (rv != SC_SUCCESS) pbuf[0] = '\0'; sc_log(card->ctx, " create path=%s", pbuf); sc_log(card->ctx, "id %04X; size %"SC_FORMAT_LEN_SIZE_T"u; type 0x%X; ef 0x%X", file->id, file->size, file->type, file->ef_structure); 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) { memcpy(&path, &file->path, sizeof(path)); if (path.len>2) path.len -= 2; if (auth_select_file(card, &path, NULL)) { sc_log(card->ctx, "Cannot select parent DF."); LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } } rv = encode_file_structure_V5(card, file, sbuf, &sendlen); 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 = (int)file->record_count; else rec_nr = 0; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, 0x00, rec_nr); apdu.data = sbuf; apdu.datalen = sendlen; apdu.lc = sendlen; 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, "Card returned error"); /* select created DF. */ if (file->type == SC_FILE_TYPE_DF) { struct sc_path tmp_path; struct sc_file *df_file = NULL; memset(&tmp_path, 0, sizeof(struct sc_path)); tmp_path.type = SC_PATH_TYPE_FILE_ID; tmp_path.value[0] = file->id >> 8; tmp_path.value[1] = file->id & 0xFF; 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); } static int auth_set_security_env(struct sc_card *card, const struct sc_security_env *env, int se_num) { struct auth_senv *auth_senv = &((struct auth_private_data *) card->drv_data)->senv; struct sc_apdu apdu; long unsigned pads = env->algorithm_flags & SC_ALGORITHM_RSA_PADS; long unsigned supported_pads = SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_PAD_ISO9796; int rv; unsigned char rsa_sbuf[3] = { 0x80, 0x01, 0xFF }; unsigned char des_sbuf[13] = { 0x80, 0x01, 0x01, 0x87, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; LOG_FUNC_CALLED(card->ctx); sc_log(card->ctx, "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); memset(auth_senv, 0, sizeof(struct auth_senv)); 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) { case SC_ALGORITHM_DES: case SC_ALGORITHM_3DES: sc_log(card->ctx, "algo SC_ALGORITHM_xDES: ref %lX, flags %lX", env->algorithm_ref, env->flags); 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 { sc_log(card->ctx, "Invalid crypto operation: %X", env->operation); LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED, "Invalid crypto operation"); } break; case SC_ALGORITHM_RSA: sc_log(card->ctx, "algo SC_ALGORITHM_RSA"); if (env->algorithm_flags & SC_ALGORITHM_RSA_HASHES) { LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED, "No support for hashes."); } 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) { 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) { 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 { sc_log(card->ctx, "Invalid crypto operation: %X", env->operation); LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } break; default: LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED, "Invalid crypto algorithm supplied"); } 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, "Card returned error"); auth_senv->algorithm = env->algorithm; LOG_FUNC_RETURN(card->ctx, rv); } static int auth_restore_security_env(struct sc_card *card, int se_num) { return SC_SUCCESS; } static int auth_compute_signature(struct sc_card *card, const unsigned char *in, size_t ilen, unsigned char * out, size_t olen) { struct sc_apdu apdu; unsigned char resp[SC_MAX_APDU_BUFFER_SIZE]; int rv; if (!card || !in || !out) { return SC_ERROR_INVALID_ARGUMENTS; } else if (ilen > 96) { sc_log(card->ctx, "Illegal input length %"SC_FORMAT_LEN_SIZE_T"u", ilen); LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "Illegal input length"); } LOG_FUNC_CALLED(card->ctx); sc_log(card->ctx, "inlen %"SC_FORMAT_LEN_SIZE_T"u, outlen %"SC_FORMAT_LEN_SIZE_T"u", ilen, olen); sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x9E, 0x9A); apdu.datalen = ilen; apdu.data = in; apdu.lc = ilen; apdu.le = olen > 256 ? 256 : olen; apdu.resp = resp; apdu.resplen = olen; 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) { sc_log(card->ctx, "Compute signature failed: invalid response length %"SC_FORMAT_LEN_SIZE_T"u", apdu.resplen); LOG_FUNC_RETURN(card->ctx, SC_ERROR_CARD_CMD_FAILED); } memcpy(out, apdu.resp, apdu.resplen); LOG_FUNC_RETURN(card->ctx, (int)apdu.resplen); } static int auth_decipher(struct sc_card *card, const unsigned char *in, size_t inlen, unsigned char *out, size_t outlen) { struct sc_apdu apdu; unsigned char resp[SC_MAX_APDU_BUFFER_SIZE]; int rv; size_t _inlen = inlen; LOG_FUNC_CALLED(card->ctx); sc_log(card->ctx, "crgram_len %"SC_FORMAT_LEN_SIZE_T"u; outlen %"SC_FORMAT_LEN_SIZE_T"u", inlen, outlen); if (!out || !outlen || inlen > SC_MAX_APDU_BUFFER_SIZE) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x80, 0x86); sc_log(card->ctx, "algorithm SC_ALGORITHM_RSA"); if (inlen % 64) { rv = SC_ERROR_INVALID_ARGUMENTS; goto done; } _inlen = inlen; if (_inlen == 256) { 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; rv = sc_transmit_apdu(card, &apdu); sc_log(card->ctx, "rv %i", rv); LOG_TEST_RET(card->ctx, rv, "APDU transmit failed"); rv = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(card->ctx, rv, "Card returned error"); _inlen -= 8; in += 8; apdu.cla &= ~0x10; } apdu.data = in; apdu.datalen = _inlen; apdu.resp = resp; apdu.resplen = SC_MAX_APDU_BUFFER_SIZE; apdu.lc = _inlen; apdu.le = _inlen; rv = sc_transmit_apdu(card, &apdu); sc_log(card->ctx, "rv %i", rv); LOG_TEST_RET(card->ctx, rv, "APDU transmit failed"); rv = sc_check_sw(card, apdu.sw1, apdu.sw2); sc_log(card->ctx, "rv %i", rv); LOG_TEST_RET(card->ctx, rv, "Card returned error"); if (outlen > apdu.resplen) outlen = apdu.resplen; memcpy(out, apdu.resp, outlen); rv = (int)outlen; done: LOG_FUNC_RETURN(card->ctx, rv); } /* Return the default AAK for this type of card */ static int auth_get_default_key(struct sc_card *card, struct sc_cardctl_default_key *data) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_NO_DEFAULT_KEY); } static int auth_encode_exponent(unsigned long exponent, unsigned char *buff, size_t buff_len) { int shift; size_t ii; for (shift=0; exponent >> (shift+8); shift += 8) ; for (ii = 0; ii=0 ; ii++, shift-=8) *(buff + ii) = (exponent >> shift) & 0xFF; if (ii==buff_len) return 0; else return (int)ii; } /* Generate key on-card */ static int auth_generate_key(struct sc_card *card, int use_sm, struct sc_cardctl_oberthur_genkey_info *data) { struct sc_apdu apdu; unsigned char sbuf[SC_MAX_APDU_BUFFER_SIZE]; struct sc_path tmp_path; int rv = 0; LOG_FUNC_CALLED(card->ctx); if (data->key_bits < 512 || data->key_bits > 2048 || (data->key_bits%0x20)!=0) { LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "Illegal key length"); } sbuf[0] = (data->id_pub >> 8) & 0xFF; sbuf[1] = data->id_pub & 0xFF; sbuf[2] = (data->id_prv >> 8) & 0xFF; sbuf[3] = data->id_prv & 0xFF; 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"); sbuf[4] = rv; rv++; } sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x46, 0x00, 0x00); apdu.resp = calloc(1, data->key_bits/8+8); if (!apdu.resp) LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); apdu.resplen = data->key_bits/8+8; apdu.lc = rv + 4; apdu.le = data->key_bits/8; apdu.data = sbuf; apdu.datalen = rv + 4; rv = sc_transmit_apdu(card, &apdu); LOG_TEST_GOTO_ERR(card->ctx, rv, "APDU transmit failed"); rv = sc_check_sw(card, apdu.sw1, apdu.sw2); 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; tmp_path.len = 2; memcpy(tmp_path.value, sbuf, 2); rv = auth_select_file(card, &tmp_path, NULL); 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_GOTO_ERR(card->ctx, rv, "auth_read_component() returned error"); apdu.resplen = rv; 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); } data->pubkey_len = apdu.resplen; free(apdu.resp); 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; } static int auth_update_component(struct sc_card *card, struct auth_update_component_info *args) { struct sc_apdu apdu; unsigned char sbuf[SC_MAX_APDU_BUFFER_SIZE + 0x10]; unsigned char ins, p1, p2; int rv, len; LOG_FUNC_CALLED(card->ctx); if (args->len > sizeof(sbuf) || args->len > 0x100) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); sc_log(card->ctx, "nn %i; len %zu", args->component, args->len); ins = 0xD8; p1 = args->component; p2 = 0x04; len = 0; sbuf[len++] = args->type; sbuf[len++] = args->len; memcpy(sbuf + len, args->data, args->len); len += args->len; 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) LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); p2 = 0; if (args->len == 24) alg = sc_evp_cipher(card->ctx, "DES-EDE"); else 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); } sbuf[len++] = 0x03; memcpy(sbuf + len, out, 3); len += 3; } else { sbuf[len++] = 0; } sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, ins, p1, p2); apdu.cla |= 0x80; apdu.data = sbuf; apdu.datalen = len; apdu.lc = len; if (args->len == 0x100) { sbuf[0] = args->type; sbuf[1] = 0x20; memcpy(sbuf + 2, args->data, 0x20); sbuf[0x22] = 0; apdu.cla |= 0x10; apdu.data = sbuf; apdu.datalen = 0x23; apdu.lc = 0x23; rv = sc_transmit_apdu(card, &apdu); apdu.cla &= ~0x10; LOG_TEST_RET(card->ctx, rv, "APDU transmit failed"); sbuf[0] = args->type; sbuf[1] = 0xE0; memcpy(sbuf + 2, args->data + 0x20, 0xE0); sbuf[0xE2] = 0; apdu.data = sbuf; apdu.datalen = 0xE3; apdu.lc = 0xE3; } rv = sc_transmit_apdu(card, &apdu); sc_mem_clear(sbuf, sizeof(sbuf)); LOG_TEST_RET(card->ctx, rv, "APDU transmit failed"); rv = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_FUNC_RETURN(card->ctx, rv); } static int auth_update_key(struct sc_card *card, struct sc_cardctl_oberthur_updatekey_info *info) { int rv, ii; LOG_FUNC_CALLED(card->ctx); 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) { struct sc_pkcs15_prkey_rsa *rsa = (struct sc_pkcs15_prkey_rsa *)info->data; struct sc_pkcs15_bignum bn[5]; sc_log(card->ctx, "Import RSA CRT"); bn[0] = rsa->p; bn[1] = rsa->q; bn[2] = rsa->iqmp; bn[3] = rsa->dmp1; bn[4] = rsa->dmq1; for (ii=0;ii<5;ii++) { struct auth_update_component_info args; memset(&args, 0, sizeof(args)); args.type = SC_CARDCTL_OBERTHUR_KEY_RSA_CRT; args.component = ii+1; args.data = bn[ii].data; args.len = bn[ii].len; 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) { rv = SC_ERROR_NOT_SUPPORTED; } else { rv = SC_ERROR_INVALID_DATA; } LOG_FUNC_RETURN(card->ctx, rv); } static int auth_card_ctl(struct sc_card *card, unsigned long cmd, void *ptr) { switch (cmd) { case SC_CARDCTL_GET_DEFAULT_KEY: return auth_get_default_key(card, (struct sc_cardctl_default_key *) ptr); case SC_CARDCTL_OBERTHUR_GENERATE_KEY: return auth_generate_key(card, 0, (struct sc_cardctl_oberthur_genkey_info *) ptr); case SC_CARDCTL_OBERTHUR_UPDATE_KEY: return auth_update_key(card, (struct sc_cardctl_oberthur_updatekey_info *) ptr); case SC_CARDCTL_OBERTHUR_CREATE_PIN: return auth_create_reference_data(card, (struct sc_cardctl_oberthur_createpin_info *) ptr); case SC_CARDCTL_GET_SERIALNR: return auth_get_serialnr(card, (struct sc_serial_number *)ptr); case SC_CARDCTL_LIFECYCLE_GET: case SC_CARDCTL_LIFECYCLE_SET: return SC_ERROR_NOT_SUPPORTED; default: LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } } static int auth_read_component(struct sc_card *card, enum SC_CARDCTL_OBERTHUR_KEY_TYPE type, int num, unsigned char *out, size_t outlen) { struct sc_apdu apdu; int rv; unsigned char resp[256]; LOG_FUNC_CALLED(card->ctx); sc_log(card->ctx, "num %i, outlen %"SC_FORMAT_LEN_SIZE_T"u, type %i", num, outlen, type); if (!outlen || type!=SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INCORRECT_PARAMETERS); sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xB4, num, 0x00); apdu.cla |= 0x80; apdu.le = outlen; apdu.resp = resp; apdu.resplen = sizeof(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, "Card returned error"); if (outlen < apdu.resplen) LOG_FUNC_RETURN(card->ctx, SC_ERROR_WRONG_LENGTH); memcpy(out, apdu.resp, apdu.resplen); LOG_FUNC_RETURN(card->ctx, (int)apdu.resplen); } static int auth_get_pin_reference (struct sc_card *card, int type, int reference, int cmd, int *out_ref) { if (!out_ref) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); switch (type) { case SC_AC_CHV: if (reference != 1 && reference != 2 && reference != 4) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_PIN_REFERENCE); *out_ref = reference; if (reference == 1 || reference == 4) if (cmd == SC_PIN_CMD_VERIFY) *out_ref |= 0x80; break; default: LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } static void auth_init_pin_info(struct sc_card *card, struct sc_pin_cmd_pin *pin, unsigned int type) { pin->offset = 0; pin->pad_char = 0xFF; pin->encoding = SC_PIN_ENCODING_ASCII; if (type == OBERTHUR_AUTH_TYPE_PIN) { pin->max_length = OBERTHUR_AUTH_MAX_LENGTH_PIN; pin->pad_length = OBERTHUR_AUTH_MAX_LENGTH_PIN; } else { pin->max_length = OBERTHUR_AUTH_MAX_LENGTH_PUK; pin->pad_length = OBERTHUR_AUTH_MAX_LENGTH_PUK; } } static int auth_pin_verify_pinpad(struct sc_card *card, int pin_reference, int *tries_left) { struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); struct sc_pin_cmd_data pin_cmd; struct sc_apdu apdu; unsigned char ffs1[0x100]; int rv; LOG_FUNC_CALLED(card->ctx); 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); /* Return SUCCESS without verifying if * PIN has been already verified and PIN pad has to be used. */ if (!rv) LOG_FUNC_RETURN(card->ctx, rv); pin_cmd.flags |= SC_PIN_CMD_NEED_PADDING; /* For Oberthur card, PIN command data length has to be 0x40. * 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. */ sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x20, 0x00, pin_reference); apdu.lc = OBERTHUR_AUTH_MAX_LENGTH_PIN; apdu.datalen = OBERTHUR_AUTH_MAX_LENGTH_PIN; apdu.data = ffs1; pin_cmd.apdu = &apdu; pin_cmd.pin_type = SC_AC_CHV; pin_cmd.cmd = SC_PIN_CMD_VERIFY; pin_cmd.flags |= SC_PIN_CMD_USE_PINPAD; pin_cmd.pin_reference = pin_reference; if (pin_cmd.pin1.min_length < 4) pin_cmd.pin1.min_length = 4; pin_cmd.pin1.max_length = 8; pin_cmd.pin1.encoding = SC_PIN_ENCODING_ASCII; pin_cmd.pin1.offset = 5; pin_cmd.pin1.data = ffs1; 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); LOG_TEST_RET(card->ctx, rv, "PIN CMD 'VERIFY' with pinpad failed"); LOG_FUNC_RETURN(card->ctx, rv); } static int auth_pin_verify(struct sc_card *card, unsigned int type, struct sc_pin_cmd_data *data, int *tries_left) { struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); int rv; LOG_FUNC_CALLED(card->ctx); if (type != SC_AC_CHV) LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED, "PIN type other then SC_AC_CHV is not supported"); data->flags |= SC_PIN_CMD_NEED_PADDING; auth_init_pin_info(card, &data->pin1, OBERTHUR_AUTH_TYPE_PIN); /* User PIN is always local. */ if (data->pin_reference == OBERTHUR_PIN_REFERENCE_USER || data->pin_reference == OBERTHUR_PIN_REFERENCE_ONETIME) data->pin_reference |= OBERTHUR_PIN_LOCAL; rv = auth_pin_is_verified(card, data->pin_reference, tries_left); sc_log(card->ctx, "auth_pin_is_verified returned rv %i", rv); /* Return if only PIN status has been asked. */ if (data->pin1.data && !data->pin1.len) LOG_FUNC_RETURN(card->ctx, rv); /* Return SUCCESS without verifying if * PIN has been already verified and PIN pad has to be used. */ if (!rv && !data->pin1.data && !data->pin1.len) LOG_FUNC_RETURN(card->ctx, rv); if (!data->pin1.data && !data->pin1.len) rv = auth_pin_verify_pinpad(card, data->pin_reference, tries_left); else rv = iso_drv->ops->pin_cmd(card, data, tries_left); LOG_FUNC_RETURN(card->ctx, rv); } static int auth_pin_is_verified(struct sc_card *card, int pin_reference, int *tries_left) { struct sc_apdu apdu; int rv; sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x20, 0, pin_reference); rv = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, rv, "APDU transmit failed"); if (tries_left && apdu.sw1 == 0x63 && (apdu.sw2 & 0xF0) == 0xC0) *tries_left = apdu.sw2 & 0x0F; /* Replace 'no tries left' with 'auth method blocked' */ if (apdu.sw1 == 0x63 && apdu.sw2 == 0xC0) { apdu.sw1 = 0x69; apdu.sw2 = 0x83; } rv = sc_check_sw(card, apdu.sw1, apdu.sw2); return rv; } static int auth_pin_change_pinpad(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_left) { struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); struct sc_pin_cmd_data pin_cmd; struct sc_apdu apdu; unsigned char ffs1[0x100]; unsigned char ffs2[0x100]; int rv, pin_reference; LOG_FUNC_CALLED(card->ctx); pin_reference = data->pin_reference & ~OBERTHUR_PIN_LOCAL; memset(ffs1, 0xFF, sizeof(ffs1)); memset(ffs2, 0xFF, sizeof(ffs2)); memset(&pin_cmd, 0, sizeof(pin_cmd)); if (data->pin1.len > OBERTHUR_AUTH_MAX_LENGTH_PIN) LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "'PIN CHANGE' failed"); if (data->pin1.data && data->pin1.len) memcpy(ffs1, data->pin1.data, data->pin1.len); pin_cmd.flags |= SC_PIN_CMD_NEED_PADDING; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x24, 0x00, pin_reference); apdu.lc = OBERTHUR_AUTH_MAX_LENGTH_PIN * 2; apdu.datalen = OBERTHUR_AUTH_MAX_LENGTH_PIN * 2; apdu.data = ffs1; pin_cmd.apdu = &apdu; pin_cmd.pin_type = SC_AC_CHV; pin_cmd.cmd = SC_PIN_CMD_CHANGE; pin_cmd.flags |= SC_PIN_CMD_USE_PINPAD; pin_cmd.pin_reference = pin_reference; if (pin_cmd.pin1.min_length < 4) pin_cmd.pin1.min_length = 4; pin_cmd.pin1.max_length = 8; pin_cmd.pin1.encoding = SC_PIN_ENCODING_ASCII; pin_cmd.pin1.offset = 5 + OBERTHUR_AUTH_MAX_LENGTH_PIN; pin_cmd.pin1.data = ffs1; pin_cmd.pin1.len = OBERTHUR_AUTH_MAX_LENGTH_PIN; pin_cmd.pin1.pad_length = 0; memcpy(&pin_cmd.pin2, &pin_cmd.pin1, sizeof(pin_cmd.pin2)); pin_cmd.pin1.offset = 5; pin_cmd.pin2.data = ffs2; rv = iso_drv->ops->pin_cmd(card, &pin_cmd, tries_left); LOG_TEST_RET(card->ctx, rv, "PIN CMD 'VERIFY' with pinpad failed"); LOG_FUNC_RETURN(card->ctx, rv); } static int auth_pin_change(struct sc_card *card, unsigned int type, struct sc_pin_cmd_data *data, int *tries_left) { 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) { /* Direct unblock style */ data->flags |= SC_PIN_CMD_NEED_PADDING; data->flags &= ~SC_PIN_CMD_USE_PINPAD; data->apdu = NULL; data->pin_reference &= ~OBERTHUR_PIN_LOCAL; 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); LOG_TEST_RET(card->ctx, rv, "CMD 'PIN CHANGE' failed"); } 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' failed: SOPIN verify with pinpad failed"); } else { LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "'PIN CHANGE' failed"); } LOG_FUNC_RETURN(card->ctx, rv); } 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_card_driver *iso_drv = sc_get_iso7816_driver(); struct sc_pin_cmd_data pin_cmd; struct sc_path tmp_path; struct sc_file *tmp_file = NULL; struct sc_apdu apdu; unsigned char puk[OBERTHUR_AUTH_MAX_LENGTH_PUK]; unsigned char ffs1[0x100]; int rv, rvv, local_pin_reference; LOG_FUNC_CALLED(card->ctx); local_pin_reference = data->pin_reference & ~OBERTHUR_PIN_LOCAL; if (data->pin_reference != OBERTHUR_PIN_REFERENCE_USER) LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "Oberthur style 'PIN RESET' failed: invalid PIN reference"); memset(&pin_cmd, 0, sizeof(pin_cmd)); memset(&tmp_path, 0, sizeof(struct sc_path)); pin_cmd.pin_type = SC_AC_CHV; pin_cmd.cmd = SC_PIN_CMD_VERIFY; 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); LOG_TEST_RET(card->ctx, rv, "Oberthur style 'PIN RESET' failed: SOPIN verify error"); sc_format_path("2000", &tmp_path); tmp_path.type = SC_PATH_TYPE_FILE_ID; 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) { 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"); if (rv != OBERTHUR_AUTH_MAX_LENGTH_PUK) LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "Oberthur style 'PIN RESET' failed"); memset(ffs1, 0xFF, sizeof(ffs1)); memcpy(ffs1, puk, rv); memset(&pin_cmd, 0, sizeof(pin_cmd)); pin_cmd.pin_type = SC_AC_CHV; pin_cmd.cmd = SC_PIN_CMD_UNBLOCK; pin_cmd.pin_reference = local_pin_reference; auth_init_pin_info(card, &pin_cmd.pin1, OBERTHUR_AUTH_TYPE_PUK); pin_cmd.pin1.data = ffs1; pin_cmd.pin1.len = OBERTHUR_AUTH_MAX_LENGTH_PUK; 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); LOG_FUNC_RETURN(card->ctx, rv); } sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x2C, 0x00, local_pin_reference); apdu.lc = OBERTHUR_AUTH_MAX_LENGTH_PIN + OBERTHUR_AUTH_MAX_LENGTH_PUK; apdu.datalen = OBERTHUR_AUTH_MAX_LENGTH_PIN + OBERTHUR_AUTH_MAX_LENGTH_PUK; apdu.data = ffs1; pin_cmd.apdu = &apdu; pin_cmd.flags |= SC_PIN_CMD_USE_PINPAD | SC_PIN_CMD_IMPLICIT_CHANGE; pin_cmd.pin1.min_length = 4; pin_cmd.pin1.max_length = 8; pin_cmd.pin1.encoding = SC_PIN_ENCODING_ASCII; pin_cmd.pin1.offset = 5; pin_cmd.pin2.data = &ffs1[OBERTHUR_AUTH_MAX_LENGTH_PUK]; pin_cmd.pin2.len = OBERTHUR_AUTH_MAX_LENGTH_PIN; pin_cmd.pin2.offset = 5 + OBERTHUR_AUTH_MAX_LENGTH_PUK; pin_cmd.pin2.min_length = 4; 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); if (rvv) sc_log(card->ctx, "%s: PIN CMD 'VERIFY' with pinpad failed", sc_strerror(rvv)); 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; LOG_FUNC_RETURN(card->ctx, rv ? rv: rvv); } static int auth_pin_reset(struct sc_card *card, unsigned int type, struct sc_pin_cmd_data *data, int *tries_left) { 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); LOG_TEST_RET(card->ctx, rv, "Oberthur style 'PIN RESET' failed"); LOG_FUNC_RETURN(card->ctx, rv); } static int auth_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_left) { int rv = SC_ERROR_INTERNAL; LOG_FUNC_CALLED(card->ctx); 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/%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); 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); 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); LOG_TEST_RET(card->ctx, rv, "CMD 'PIN VERIFY' failed"); break; default: LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED, "Unsupported PIN operation"); } LOG_FUNC_RETURN(card->ctx, rv); } static int auth_create_reference_data (struct sc_card *card, struct sc_cardctl_oberthur_createpin_info *args) { struct sc_apdu apdu; struct sc_pin_cmd_pin pin_info, puk_info; int rv, len; unsigned char sbuf[SC_MAX_APDU_BUFFER_SIZE]; LOG_FUNC_CALLED(card->ctx); sc_log(card->ctx, "PIN reference %i", args->ref); if (args->type != SC_AC_CHV) LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED, "Unsupported PIN type"); if (args->pin_tries < 1 || !args->pin || !args->pin_len) LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "Invalid PIN options"); if (args->ref != OBERTHUR_PIN_REFERENCE_USER && args->ref != OBERTHUR_PIN_REFERENCE_PUK) LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_PIN_REFERENCE, "Invalid PIN reference"); auth_init_pin_info(card, &puk_info, OBERTHUR_AUTH_TYPE_PUK); auth_init_pin_info(card, &pin_info, OBERTHUR_AUTH_TYPE_PIN); if (args->puk && args->puk_len && (args->puk_len%puk_info.pad_length)) LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "Invalid PUK options"); len = 0; sc_log(card->ctx, "len %i", len); sbuf[len++] = args->pin_tries; sbuf[len++] = pin_info.pad_length; sc_log(card->ctx, "len %i", len); memset(sbuf + len, pin_info.pad_char, pin_info.pad_length); memcpy(sbuf + len, args->pin, args->pin_len); len += pin_info.pad_length; sc_log(card->ctx, "len %i", 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); memcpy(sbuf + len, args->puk, args->puk_len); len += args->puk_len; } sc_log(card->ctx, "len %i", len); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x24, 1, args->ref & ~OBERTHUR_PIN_LOCAL); apdu.data = sbuf; apdu.datalen = len; apdu.lc = len; rv = sc_transmit_apdu(card, &apdu); sc_mem_clear(sbuf, sizeof(sbuf)); LOG_TEST_RET(card->ctx, rv, "APDU transmit failed"); rv = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_FUNC_RETURN(card->ctx, rv); } static int auth_logout(struct sc_card *card) { struct sc_apdu apdu; int ii, rv = 0, pin_ref; int reset_flag = 0x20; 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"); sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x2E, 0x00, 0x00); apdu.cla = 0x80; 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); } static int write_publickey (struct sc_card *card, unsigned int offset, const unsigned char *buf, size_t count) { struct auth_update_component_info args; struct sc_pkcs15_pubkey_rsa key; int ii, rv; size_t len = 0, der_size = 0; LOG_FUNC_CALLED(card->ctx); 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"); len = offset+count > sizeof(rsa_der) ? sizeof(rsa_der) - offset : count; memcpy(rsa_der + offset, buf, len); rsa_der_len = offset + len; 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]; else der_size = rsa_der[1]; } 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, (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"); memset(&args, 0, sizeof(args)); args.type = SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC; args.component = 1; 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"); memset(&args, 0, sizeof(args)); args.type = SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC; args.component = 2; args.data = key.exponent.data; args.len = key.exponent.len; rv = auth_update_component(card, &args); LOG_TEST_RET(card->ctx, rv, "Update component failed"); LOG_FUNC_RETURN(card->ctx, (int)len); } static int auth_update_binary(struct sc_card *card, unsigned int offset, const unsigned char *buf, size_t count, unsigned long flags) { 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", 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) { 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) { struct auth_update_component_info args; memset(&args, 0, sizeof(args)); args.type = SC_CARDCTL_OBERTHUR_KEY_DES; args.data = (unsigned char *)buf; args.len = count; rv = auth_update_component(card, &args); } else { rv = iso_ops->update_binary(card, offset, buf, count, 0); } LOG_FUNC_RETURN(card->ctx, rv); } static int auth_read_binary(struct sc_card *card, unsigned int offset, unsigned char *buf, size_t count, unsigned long *flags) { int rv; 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 ? *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) { int jj; unsigned char resp[256]; size_t resp_len, out_len; struct sc_pkcs15_pubkey_rsa key; resp_len = sizeof(resp); rv = auth_read_component(card, SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC, 2, resp, resp_len); LOG_TEST_RET(card->ctx, rv, "read component failed"); for (jj=0; jjctx, rv, "Cannot read RSA public key component"); bn[1].data = calloc(1, rv); if (!bn[1].data) { rv = SC_ERROR_OUT_OF_MEMORY; goto err; } bn[1].len = rv; memcpy(bn[1].data, resp, rv); key.exponent = bn[0]; key.modulus = bn[1]; 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 { if (out_len < offset) { rv = SC_ERROR_UNKNOWN_DATA_RECEIVED; goto err; } sz = MIN(out_len - offset, count); memcpy(buf, out + offset, sz); sc_log_hex(card->ctx, "write_publickey", buf, sz); rv = (int)sz; } } else { rv = iso_ops->read_binary(card, offset, buf, count, 0); } err: free(bn[0].data); free(bn[1].data); free(out); LOG_FUNC_RETURN(card->ctx, rv); } static int 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; int rv = 0; unsigned char recvbuf[SC_MAX_APDU_BUFFER_SIZE]; sc_log(card->ctx, "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.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)); 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, (int)apdu.resplen); } static int auth_delete_record(struct sc_card *card, unsigned int nr_rec) { struct sc_apdu apdu; int rv = 0; LOG_FUNC_CALLED(card->ctx); sc_log(card->ctx, "auth_delete_record(): nr_rec %i", nr_rec); sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x32, nr_rec, 0x04); apdu.cla = 0x80; 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_FUNC_RETURN(card->ctx, rv); } static int auth_get_serialnr(struct sc_card *card, struct sc_serial_number *serial) { if (!serial) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); if (card->serialnr.len==0) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); memcpy(serial, &card->serialnr, sizeof(*serial)); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } static const struct sc_card_error auth_warnings[] = { { 0x6282, SC_SUCCESS, "ignore warning 'End of file or record reached before reading Ne bytes'" }, {0, 0, NULL}, }; static int 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)) { sc_log(card->ctx, "%s", auth_warnings[ii].errorstr); return auth_warnings[ii].errorno; } } return iso_ops->check_sw(card, sw1, sw2); } static struct sc_card_driver * sc_get_driver(void) { if (iso_ops == NULL) iso_ops = sc_get_iso7816_driver()->ops; auth_ops = *iso_ops; auth_ops.match_card = auth_match_card; auth_ops.init = auth_init; auth_ops.finish = auth_finish; auth_ops.select_file = auth_select_file; auth_ops.list_files = auth_list_files; auth_ops.delete_file = auth_delete_file; auth_ops.create_file = auth_create_file; auth_ops.read_binary = auth_read_binary; auth_ops.update_binary = auth_update_binary; auth_ops.read_record = auth_read_record; auth_ops.delete_record = auth_delete_record; auth_ops.card_ctl = auth_card_ctl; auth_ops.set_security_env = auth_set_security_env; auth_ops.restore_security_env = auth_restore_security_env; auth_ops.compute_signature = auth_compute_signature; auth_ops.decipher = auth_decipher; auth_ops.process_fci = auth_process_fci; auth_ops.pin_cmd = auth_pin_cmd; auth_ops.logout = auth_logout; auth_ops.check_sw = auth_check_sw; return &auth_drv; } struct sc_card_driver * sc_get_oberthur_driver(void) { return sc_get_driver(); } #endif /* ENABLE_OPENSSL */ OpenSC-0.26.1/src/libopensc/card-openpgp.c000066400000000000000000003562451474147347300202540ustar00rootroot00000000000000/* * card-openpgp.c: Support for OpenPGP card * * 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 */ /* * Specifications: * (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 */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "internal.h" #include "asn1.h" #include "cardctl.h" #include "errors.h" #ifdef ENABLE_OPENSSL #include #endif /* ENABLE_OPENSSL */ #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 = { "OpenPGP card", "openpgp", &pgp_ops, NULL, 0, NULL }; 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}}, 256}, /* curve25519 for encryption => CKK_EC_MONTGOMERY */ {{{1, 3, 6, 1, 4, 1, 11591, 15, 1, -1}}, 256}, /* ed25519 for signatures => CKK_EC_EDWARDS */ /* v3.0+ supports: [RFC 4880 & 6637] 0x12 = ECDH, 0x13 = ECDSA */ {{{1, 2, 840, 10045, 3, 1, 7, -1}}, 256}, /* ansiX9p256r1 */ {{{1, 3, 132, 0, 34, -1}}, 384}, /* ansiX9p384r1 */ {{{1, 3, 132, 0, 35, -1}}, 521}, /* ansiX9p521r1 */ {{{1, 3, 36, 3, 3, 2, 8, 1, 1, 7, -1}}, 256}, /* brainpoolP256r1 */ {{{1, 3, 36, 3, 3, 2, 8, 1, 1, 11, -1}}, 384}, /* brainpoolP384r1 */ {{{1, 3, 36, 3, 3, 2, 8, 1, 1, 13, -1}}, 512}, /* brainpoolP512r1 */ {{{-1}}, 0} /* This entry must not be touched. */ }; static pgp_ec_curves_t *ec_curves_openpgp = ec_curves_openpgp34 + 2; struct sc_object_id curve25519_oid = {{1, 3, 6, 1, 4, 1, 3029, 1, 5, 1, -1}}; /* 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}, /* ansiX9p256r1 */ {{{1, 3, 132, 0, 10, -1}}, 256}, /* secp256k1 */ {{{1, 3, 6, 1, 4, 1, 3029, 1, 5, 1, -1}}, 256}, /* curve25519 for encryption => CKK_EC_MONTGOMERY */ {{{1, 3, 6, 1, 4, 1, 11591, 15, 1, -1}}, 256}, /* ed25519 for signatures => CKK_EC_EDWARDS */ {{{-1}}, 0} /* This entry must not be touched. */ }; /* * 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. */ static int pgp_get_card_features(sc_card_t *card); static int pgp_finish(sc_card_t *card); 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 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); 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 }, { 0x0065, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, sc_get_data, NULL }, { 0x006e, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, sc_get_data, NULL }, { 0x0073, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, NULL, NULL }, { 0x007a, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, sc_get_data, NULL }, { 0x0081, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, NULL }, { 0x0082, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, NULL }, { 0x0093, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, NULL }, { 0x00c0, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, NULL }, { 0x00c1, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, NULL }, { 0x00c2, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, NULL }, { 0x00c3, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, NULL }, { 0x00c4, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data }, { 0x00c5, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data }, { 0x00c6, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data }, { 0x00c7, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data }, { 0x00c8, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data }, { 0x00c9, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data }, { 0x00ca, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data }, { 0x00cb, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data }, { 0x00cc, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data }, { 0x00cd, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data }, { 0x00ce, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data }, { 0x00cf, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data }, { 0x00d0, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data }, { 0x00e0, CONSTRUCTED, READ_NEVER | WRITE_PIN3, NULL, sc_put_data }, { 0x00e1, CONSTRUCTED, READ_NEVER | WRITE_PIN3, NULL, sc_put_data }, { 0x00e2, CONSTRUCTED, READ_NEVER | WRITE_PIN3, NULL, sc_put_data }, { 0x0101, SIMPLE, READ_ALWAYS | WRITE_PIN2, sc_get_data, sc_put_data }, { 0x0102, SIMPLE, READ_ALWAYS | WRITE_PIN3, sc_get_data, sc_put_data }, { 0x0103, SIMPLE, READ_PIN2 | WRITE_PIN2, sc_get_data, sc_put_data }, { 0x0104, SIMPLE, READ_PIN3 | WRITE_PIN3, sc_get_data, sc_put_data }, { 0x3f00, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, NULL, NULL }, { 0x5f2d, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data }, { 0x5f35, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data }, { 0x5f50, SIMPLE, READ_ALWAYS | WRITE_PIN3, sc_get_data, sc_put_data }, { 0x7f49, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, NULL, NULL }, { DO_AUTH, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, pgp_get_pubkey, NULL }, { DO_AUTH_SYM, SIMPLE, READ_ALWAYS | WRITE_PIN3, pgp_get_pubkey_pem, NULL }, { DO_SIGN, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, pgp_get_pubkey, NULL }, { DO_SIGN_SYM, SIMPLE, READ_ALWAYS | WRITE_PIN3, pgp_get_pubkey_pem, NULL }, { DO_ENCR, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, pgp_get_pubkey, NULL }, { DO_ENCR_SYM, SIMPLE, READ_ALWAYS | WRITE_PIN3, pgp_get_pubkey_pem, NULL }, { 0, 0, 0, NULL, NULL }, }; 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 ****/ { 0x00f9, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, 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 }, { 0x005e, SIMPLE, READ_ALWAYS | WRITE_PIN3, sc_get_data, sc_put_data }, { 0x0065, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, sc_get_data, NULL }, { 0x006e, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, sc_get_data, NULL }, { 0x0073, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, NULL, NULL }, { 0x007a, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, sc_get_data, NULL }, { 0x0081, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, NULL }, { 0x0082, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, NULL }, { 0x0093, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, NULL }, { 0x00c0, SIMPLE, READ_ALWAYS | WRITE_NEVER, NULL, NULL }, { 0x00c1, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data }, { 0x00c2, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data }, { 0x00c3, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data }, { 0x00c4, SIMPLE, READ_ALWAYS | WRITE_PIN3, sc_get_data, sc_put_data }, { 0x00c5, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data }, { 0x00c6, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data }, { 0x00c7, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data }, { 0x00c8, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data }, { 0x00c9, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data }, { 0x00ca, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data }, { 0x00cb, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data }, { 0x00cc, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data }, { 0x00cd, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data }, { 0x00ce, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data }, { 0x00cf, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data }, { 0x00d0, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data }, { 0x00d1, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data }, { 0x00d2, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data }, { 0x00d3, SIMPLE, READ_NEVER | WRITE_PIN3, NULL, sc_put_data }, { 0x00f4, CONSTRUCTED, READ_NEVER | WRITE_PIN3, NULL, sc_put_data }, { 0x0101, SIMPLE, READ_ALWAYS | WRITE_PIN2, sc_get_data, sc_put_data }, { 0x0102, SIMPLE, READ_ALWAYS | WRITE_PIN3, sc_get_data, sc_put_data }, { 0x0103, SIMPLE, READ_PIN2 | WRITE_PIN2, sc_get_data, sc_put_data }, { 0x0104, SIMPLE, READ_PIN3 | WRITE_PIN3, sc_get_data, sc_put_data }, { 0x3f00, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, NULL, NULL }, { 0x5f2d, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data }, { 0x5f35, SIMPLE, READ_ALWAYS | WRITE_PIN3, NULL, sc_put_data }, { 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 }, /* 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 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 }, { DO_SIGN, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, pgp_get_pubkey, NULL }, { DO_SIGN_SYM, SIMPLE, READ_ALWAYS | WRITE_PIN3, pgp_get_pubkey_pem, NULL }, { DO_ENCR, CONSTRUCTED, READ_ALWAYS | WRITE_NEVER, pgp_get_pubkey, NULL }, { DO_ENCR_SYM, SIMPLE, READ_ALWAYS | WRITE_PIN3, pgp_get_pubkey_pem, NULL }, { 0, 0, 0, NULL, NULL }, }; // clang-format on 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; /** * 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; if (file != NULL) { /* explicitly get the full aid */ r = sc_get_data(card, 0x004F, file->name, sizeof file->name); file->namelen = MAX(r, 0); } return r; } /** * ABI: check if card's ATR matches one of driver's * 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; LOG_FUNC_RETURN(card->ctx, 1); } else { sc_path_t partial_aid; sc_file_t *file = NULL; /* select application "OpenPGP" */ sc_format_path("D276:0001:2401", &partial_aid); partial_aid.type = SC_PATH_TYPE_DF_NAME; /* 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); } } LOG_FUNC_RETURN(card->ctx, 0); } /* 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 & allocate private data. */ static int pgp_init(sc_card_t *card) { struct pgp_priv_data *priv; sc_path_t path; sc_file_t *file = NULL; int r, i; LOG_FUNC_CALLED(card->ctx); priv = calloc (1, sizeof *priv); if (!priv) LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); card->drv_data = priv; card->cla = 0x00; /* select application "OpenPGP" */ 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, SC_ERROR_INVALID_CARD); } /* defensive programming check */ if (!file) { pgp_finish(card); LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_CARD); } if (file->namelen != 16) { /* explicitly get the full aid */ r = get_full_pgp_aid(card, file); if (r < 0) { sc_file_free(file); pgp_finish(card); LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_CARD); } } /* read information from AID */ if (file->namelen == 16) { 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 = 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; /* 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 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); } /* select MF */ priv->current = priv->mf; 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 */ 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) && (strcmp(card->ctx->app_name, "pkcs15-init") == 0)) { unsigned long flags_rsa, flags_ecc, ext_flags; /* 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; 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++) { _sc_card_add_ec_alg(card, priv->ec_curves[i].size, flags_ecc, ext_flags, &priv->ec_curves[i].oid); } 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: parse historic bytes to get card capabilities. */ static void pgp_parse_hist_bytes(sc_card_t *card, u8 *ctlv, size_t ctlv_len) { struct pgp_priv_data *priv = DRVDATA(card); 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 (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 (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_keygen_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.oid_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.oid_len = blob->len - 2; else key_info->u.ec.oid_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.oid_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_keygen_info_t key_info, unsigned int do_num) { if (!sc_compare_oid(&key_info.u.ec.oid, &curve25519_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, 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_keygen_info_t key_info, unsigned int do_num) { unsigned long flags = 0, ext_flags = 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, 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; } /** * 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)) { 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)) { 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_keygen_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)) { /* v2.0+: bit 0x04 in first byte means "algorithm attributes changeable" */ if ((blob->data[0] & 0x04) && (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) priv->ext_caps |= EXT_CAP_PRIVATE_DO; /* bit 0x10 in first byte means "support for CHV status byte changeable" */ if (blob->data[0] & 0x10) priv->ext_caps |= EXT_CAP_C4_CHANGEABLE; /* bit 0x20 in first byte means "support for Key Import" */ if (blob->data[0] & 0x20) priv->ext_caps |= EXT_CAP_KEY_IMPORT; /* bit 0x40 in first byte means "support for Get Challenge" */ if (blob->data[0] & 0x40) { card->caps |= SC_CARD_CAP_RNG; priv->ext_caps |= EXT_CAP_GET_CHALLENGE; } /* v2.0+: bit 0x80 in first byte means "support Secure Messaging" */ if ((blob->data[0] & 0x80) && (priv->bcd_version >= OPENPGP_CARD_2_0)) priv->ext_caps |= EXT_CAP_SM; if ((priv->bcd_version >= OPENPGP_CARD_2_0) && (blob->len >= 10)) { /* v2.0+: max. challenge size is at bytes 3-4 */ priv->max_challenge_size = bebytes2ushort(blob->data + 2); /* v2.0+: max. cert size it at bytes 5-6 */ priv->max_cert_size = bebytes2ushort(blob->data + 4); 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; } } } /* get max. PIN length from "CHV status bytes" DO */ if ((pgp_get_blob(card, blob73, 0x00c4, &blob) >= 0) && (blob->data != NULL) && (blob->len > 1)) { /* 2nd byte in "CHV status bytes" DO means "max. PIN length" */ card->max_pin_len = blob->data[1]; } 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); } } /* 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++) { sc_cardctl_openpgp_keygen_info_t key_info; 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) && (pgp_parse_algo_attr_blob(card, blob, &key_info) >= 0)) { _pgp_add_algo(card, key_info, i); } } } LOG_FUNC_RETURN(card->ctx, handled_algos); } /** * ABI: terminate driver & free private data. */ static int pgp_finish(sc_card_t *card) { if (card != NULL) { struct pgp_priv_data *priv = DRVDATA(card); if (priv != NULL) { /* delete fake file hierarchy */ pgp_free_blobs(priv->mf); /* delete private data */ free(priv); } card->drv_data = NULL; } return SC_SUCCESS; } /** * Internal: fill a blob's data. */ static int pgp_set_blob(pgp_blob_t *blob, const u8 *data, size_t len) { if (blob->data) free(blob->data); blob->data = NULL; blob->len = 0; blob->status = 0; if (len > 0) { void *tmp = calloc(1, len); if (tmp == NULL) return SC_ERROR_OUT_OF_MEMORY; blob->data = tmp; blob->len = (unsigned int)len; if (data != NULL) memcpy(blob->data, data, len); } if (blob->file) blob->file->size = len; return SC_SUCCESS; } /** * Internal: implement Access Control List for emulated file. * The Access Control is derived from the DO access permission. **/ static void 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; /* Write access */ switch (info->access & WRITE_MASK) { case WRITE_NEVER: method = SC_AC_NEVER; break; case WRITE_PIN1: method = SC_AC_CHV; key_ref = 0x01; break; case WRITE_PIN2: method = SC_AC_CHV; key_ref = 0x02; break; case WRITE_PIN3: method = SC_AC_CHV; key_ref = 0x03; break; } if (method != SC_AC_NONE || key_ref != SC_AC_KEY_REF_NONE) { sc_file_add_acl_entry(file, SC_AC_OP_WRITE, method, key_ref); sc_file_add_acl_entry(file, SC_AC_OP_UPDATE, method, key_ref); sc_file_add_acl_entry(file, SC_AC_OP_DELETE, method, key_ref); sc_file_add_acl_entry(file, SC_AC_OP_CREATE, method, key_ref); } else { /* When SC_AC_OP_DELETE is absent, we need to provide * SC_AC_OP_DELETE_SELF for sc_pkcs15init_delete_by_path() */ sc_file_add_acl_entry(file, SC_AC_OP_DELETE_SELF, method, key_ref); } method = SC_AC_NONE; key_ref = SC_AC_KEY_REF_NONE; /* Read access */ switch (info->access & READ_MASK) { case READ_NEVER: method = SC_AC_NEVER; break; case READ_PIN1: method = SC_AC_CHV; key_ref = 0x01; break; case READ_PIN2: method = SC_AC_CHV; key_ref = 0x02; break; case READ_PIN3: method = SC_AC_CHV; key_ref = 0x03; break; } if (method != SC_AC_NONE || key_ref != SC_AC_KEY_REF_NONE) { sc_file_add_acl_entry(file, SC_AC_OP_READ, method, key_ref); } } /** * Internal: append a blob to the list of children of a given parent blob. */ static pgp_blob_t * pgp_new_blob(sc_card_t *card, pgp_blob_t *parent, unsigned int file_id, sc_file_t *file) { pgp_blob_t *blob = NULL; if (file == NULL) return NULL; if ((blob = calloc(1, sizeof(pgp_blob_t))) != NULL) { struct pgp_priv_data *priv = DRVDATA(card); pgp_do_info_t *info; blob->file = file; blob->file->type = SC_FILE_TYPE_WORKING_EF; /* default */ blob->file->ef_structure = SC_FILE_EF_TRANSPARENT; blob->file->id = file_id; blob->id = file_id; blob->parent = parent; if (parent != NULL) { pgp_blob_t **p; /* set file's path = parent's path + file's id */ blob->file->path = parent->file->path; sc_append_file_id(&blob->file->path, file_id); /* append blob to list of parent's children */ for (p = &parent->files; *p != NULL; p = &(*p)->next) ; *p = blob; } else { char path[10] = "0000"; /* long enough */ /* no parent: set file's path = file's id */ 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 */ for (info = priv->pgp_objects; (info != NULL) && (info->id > 0); info++) { if (info->id == file_id) { blob->info = info; blob->file->type = blob->info->type; pgp_attach_acl(card, blob->file, info); break; } } } return blob; } /** * Internal: free a blob including its content. */ static void pgp_free_blob(pgp_blob_t *blob) { if (blob) { if (blob->parent) { pgp_blob_t **p; /* remove blob from list of parent's children */ for (p = &blob->parent->files; *p != NULL && *p != blob; p = &(*p)->next) ; if (*p == blob) *p = blob->next; } sc_file_free(blob->file); if (blob->data) free(blob->data); free(blob); } } /** * Internal: iterate through the blob tree, calling pgp_free_blob for each blob. */ static void pgp_free_blobs(pgp_blob_t *blob) { if (blob) { pgp_blob_t *child = blob->files; while (child != NULL) { pgp_blob_t *next = child->next; pgp_free_blobs(child); child = next; } pgp_free_blob(blob); } } /** * Internal: read a blob's contents from card. */ static int pgp_read_blob(sc_card_t *card, pgp_blob_t *blob) { struct pgp_priv_data *priv = DRVDATA(card); if (blob->data != NULL) return SC_SUCCESS; if (blob->info == NULL) return blob->status; if (blob->info->get_fn) { /* readable, top-level DO */ u8 buffer[MAX_OPENPGP_DO_SIZE]; size_t buf_len = sizeof(buffer); int r = SC_SUCCESS; /* buffer length for certificate */ if (blob->id == DO_CERT && priv->max_cert_size > 0) { buf_len = MIN(priv->max_cert_size, sizeof(buffer)); } /* buffer length for Gnuk pubkey */ if (card->type == SC_CARD_TYPE_OPENPGP_GNUK && (blob->id == DO_AUTH || blob->id == DO_SIGN || blob->id == DO_ENCR || blob->id == DO_AUTH_SYM || blob->id == DO_SIGN_SYM || blob->id == DO_ENCR_SYM)) { buf_len = MIN(MAXLEN_RESP_PUBKEY_GNUK, sizeof(buffer)); } r = blob->info->get_fn(card, blob->id, buffer, buf_len); if (r < 0) { /* an error occurred */ blob->status = r; return r; } return pgp_set_blob(blob, buffer, r); } else { /* un-readable DO or part of a constructed DO */ return SC_SUCCESS; } } /** * Internal: enumerate contents of a data blob. * The OpenPGP card has a TLV encoding according ASN.1 BER-encoding rules. */ static int 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; if ((r = pgp_read_blob(card, blob)) < 0) return r; in = blob->data; while ((int) blob->len > (in - blob->data)) { unsigned int cla, tag, tmptag; size_t len; 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 == 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; } /* undo ASN1's split of tag & class */ for (tmptag = tag; tmptag > 0x0FF; tmptag >>= 8) { 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 * entire blob. Example: Yubikey Neo */ if (tag == blob->id) { in = data; continue; } /* create fake file system hierarchy by * using constructed DOs as DF */ 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; } in = data + len; } return SC_SUCCESS; } /** * Internal: find a blob by ID below a given parent, filling its contents when necessary. */ static int pgp_get_blob(sc_card_t *card, pgp_blob_t *blob, unsigned int id, pgp_blob_t **ret) { pgp_blob_t *child; int r; if ((r = pgp_enumerate_blob(card, blob)) < 0) return r; for (child = blob->files; child; child = child->next) { if (child->id == id) { (void) pgp_read_blob(card, child); *ret = child; return SC_SUCCESS; } } /* This part is for "NOT FOUND" cases */ /* Special case: * Gnuk does not have default value for children of DO 65 (DOs 5B, 5F2D, 5F35) * So, if these blob was not found, we create it. */ if (blob->id == DO_CARDHOLDER && (id == DO_NAME || id == DO_LANG_PREF || id == DO_SEX)) { sc_log(card->ctx, "Create blob %X under %X", id, blob->id); child = pgp_new_blob(card, blob, id, sc_file_new()); if (child) { pgp_set_blob(child, NULL, 0); *ret = child; return SC_SUCCESS; } else sc_log(card->ctx, "Not enough memory to create blob for DO %X", id); } return SC_ERROR_FILE_NOT_FOUND; } /** * Internal: search recursively for a blob by ID below a given root. */ static int pgp_seek_blob(sc_card_t *card, pgp_blob_t *root, unsigned int id, pgp_blob_t **ret) { pgp_blob_t *child; int r; if ((r = pgp_get_blob(card, root, id, ret)) == 0) /* the sought blob is right under root */ return r; /* not found, seek deeper */ 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 && child->info->type == SIMPLE) || child->id == DO_CERT) continue; r = pgp_seek_blob(card, child, id, ret); if (r == 0) return r; } return SC_ERROR_FILE_NOT_FOUND; } /** * Internal: find a blob by tag - pgp_seek_blob with optimizations. */ static pgp_blob_t * pgp_find_blob(sc_card_t *card, unsigned int tag) { struct pgp_priv_data *priv = DRVDATA(card); pgp_blob_t *blob = NULL; int r; /* check if current selected blob is which we want to test */ if (priv->current->id == tag) { return priv->current; } /* look for the blob representing the DO */ r = pgp_seek_blob(card, priv->mf, tag, &blob); if (r < 0) { sc_log(card->ctx, "Failed to seek the blob representing the tag %04X. Error %d.", tag, r); return NULL; } return blob; } /** * Internal: get info for a specific tag. */ static pgp_do_info_t * pgp_get_info_by_tag(sc_card_t *card, unsigned int tag) { struct pgp_priv_data *priv = DRVDATA(card); pgp_do_info_t *info; for (info = priv->pgp_objects; (info != NULL) && (info->id > 0); info++) if (tag == info->id) return info; return NULL; } /** * Internal: strip out the parts of PKCS15 file layout in the path. * Get the reduced version which is understood by the OpenPGP card driver. * Return the index whose preceding part will be ignored. **/ static unsigned int pgp_strip_path(sc_card_t *card, const sc_path_t *path) { unsigned int start_point = 0; /* start_point will move through the path string */ if (path->len == 0) return 0; /* ignore 3F00 (MF) at the beginning */ start_point = (memcmp(path->value, "\x3f\x00", 2) == 0) ? 2 : 0; /* strip path of PKCS15-App DF (5015) */ start_point += (memcmp(path->value + start_point, "\x50\x15", 2) == 0) ? 2 : 0; return start_point; } /** * 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) { struct pgp_priv_data *priv = DRVDATA(card); pgp_blob_t *blob; unsigned int path_start = 0; unsigned int n; sc_path_t dummy_path; LOG_FUNC_CALLED(card->ctx); if (path->type == SC_PATH_TYPE_DF_NAME) LOG_FUNC_RETURN(card->ctx, iso_ops->select_file(card, path, ret)); if (path->len < 2 || (path->len & 1)) LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "invalid path length"); if (path->type == SC_PATH_TYPE_FILE_ID && path->len != 2) LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "invalid path type"); /* Due to pkcs15init implementation, sometimes a file at path "11001101" * need to be written (one use case is when importing key&cert from p12 file). * This file does not exist in OpenPGP but pkcs15 requires that * writing this file must be successful. * So, we pretend that selecting & writing this file is successful. * The "11001101"is defined in sc_pkcs15emu_get_df() function, pkcs15-sync.c file. */ sc_format_path("11001101", &dummy_path); if (sc_compare_path(path, &dummy_path)) { if (ret != NULL) { *ret = sc_file_new(); /* One use case of this dummy file is after writing certificate in pkcs15init. * So we set its size to be the same as max certificate size the card supports. */ (*ret)->size = priv->max_cert_size; } LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } /* ignore explicitly mentioned MF at the path's beginning */ path_start = pgp_strip_path(card, path); /* starting with the MF ... */ blob = priv->mf; /* ... recurse through the tree following the path */ for (n = path_start; n < path->len; n += 2) { unsigned int id = bebytes2ushort(path->value + n); int r = pgp_get_blob(card, blob, id, &blob); /* 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) /* No need to return file */ LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); /* Else, need to return file */ *ret = sc_file_new(); (*ret)->size = priv->max_cert_size; LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } if (r < 0) { /* failure */ LOG_FUNC_RETURN(card->ctx, r); } } /* success: select file = set "current" pointer to blob found */ priv->current = blob; if (ret) sc_file_dup(ret, blob->file); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } /** * 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) { struct pgp_priv_data *priv = DRVDATA(card); pgp_blob_t *blob; unsigned int k; int r; LOG_FUNC_CALLED(card->ctx); /* jump to selected file */ blob = priv->current; if (blob->file->type != SC_FILE_TYPE_DF) LOG_TEST_RET(card->ctx, SC_ERROR_OBJECT_NOT_VALID, "invalid file type"); if ((r = pgp_enumerate_blob(card, blob)) < 0) LOG_FUNC_RETURN(card->ctx, r); for (k = 0, blob = blob->files; blob != NULL; blob = blob->next) { if (blob->info != NULL && (blob->info->access & READ_MASK) != READ_NEVER) { if (k + 2 > buflen) LOG_FUNC_RETURN(card->ctx, SC_ERROR_BUFFER_TOO_SMALL); ushort2bebytes(buf + k, blob->id); k += 2; } } LOG_FUNC_RETURN(card->ctx, k); } /** * 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) { struct pgp_priv_data *priv = DRVDATA(card); pgp_blob_t *blob; int r; LOG_FUNC_CALLED(card->ctx); /* jump to selected file */ blob = priv->current; if (blob == NULL) LOG_FUNC_RETURN(card->ctx, SC_ERROR_FILE_NOT_FOUND); if (blob->file->type != SC_FILE_TYPE_WORKING_EF) LOG_FUNC_RETURN(card->ctx, SC_ERROR_FILE_NOT_FOUND); if ((r = pgp_read_blob(card, blob)) < 0) LOG_FUNC_RETURN(card->ctx, r); if (idx > blob->len) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INCORRECT_PARAMETERS); if (idx + count > blob->len) count = blob->len - idx; memcpy(buf, blob->data + idx, count); LOG_FUNC_RETURN(card->ctx, (int)count); } /** * 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) { sc_apdu_t apdu; u8 apdu_case = (card->type == SC_CARD_TYPE_OPENPGP_GNUK) ? SC_APDU_CASE_4_SHORT : SC_APDU_CASE_4; u8 idbuf[2]; int r; sc_log(card->ctx, "called, tag=%04x\n", tag); sc_format_apdu(card, &apdu, apdu_case, 0x47, 0x81, 0); apdu.lc = 2; apdu.data = ushort2bebytes(idbuf, tag); apdu.datalen = 2; apdu.le = ((buf_len >= 256) && !(card->caps & SC_CARD_CAP_APDU_EXT)) ? 256 : buf_len; apdu.resp = buf; apdu.resplen = buf_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); LOG_TEST_RET(card->ctx, r, "Card returned error"); LOG_FUNC_RETURN(card->ctx, (int)apdu.resplen); } /** * 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, *pubkey_blob, *blob6e, *blob73, *aa_blob; sc_pkcs15_pubkey_t p15pubkey; sc_cardctl_openpgp_keygen_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) LOG_TEST_RET(card->ctx, r, "error getting elements"); /* 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.eddsa.pubkey.value = pubkey_blob->data; p15pubkey.u.eddsa.pubkey.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.eddsa.pubkey.value = pubkey_blob->data; p15pubkey.u.eddsa.pubkey.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.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 */ } else if (p15pubkey.algorithm == SC_ALGORITHM_EDDSA || p15pubkey.algorithm == SC_ALGORITHM_XEDDSA) { p15pubkey.u.eddsa.pubkey.value = NULL; p15pubkey.u.eddsa.pubkey.len = 0; } sc_pkcs15_erase_pubkey(&p15pubkey); LOG_TEST_RET(card->ctx, r, "public key encoding failed"); if (len > buf_len) len = buf_len; memcpy(buf, data, len); free(data); LOG_FUNC_RETURN(card->ctx, (int)len); } /** * 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) { sc_apdu_t apdu; int r; LOG_FUNC_CALLED(card->ctx); sc_format_apdu(card, &apdu, SC_APDU_CASE_2, 0xCA, tag >> 8, tag); apdu.le = ((buf_len >= 256) && !(card->caps & SC_CARD_CAP_APDU_EXT)) ? 256 : buf_len; apdu.resp = buf; apdu.resplen = buf_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); /* 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 (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. */ static int gnuk_write_certificate(sc_card_t *card, const u8 *buf, size_t length) { size_t i = 0; sc_apdu_t apdu; int r = SC_SUCCESS; LOG_FUNC_CALLED(card->ctx); /* If null data is passed, delete certificate */ if (buf == NULL || length == 0) { sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0xD6, 0x85, 0); r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); /* Check response */ 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 */ /* Split data to segments of 256 bytes. Send each segment via command chaining, * with particular P1 byte for each segment */ for (i = 0; i*256 < length; i++) { u8 *part = (u8 *)buf + i*256; size_t plen = MIN(length - i*256, 256); u8 roundbuf[256]; /* space to build APDU data with even length for Gnuk */ sc_log(card->ctx, "Write part %"SC_FORMAT_LEN_SIZE_T"u from offset 0x%"SC_FORMAT_LEN_SIZE_T"X, len %"SC_FORMAT_LEN_SIZE_T"u", 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 : (int)i, 0); apdu.flags |= SC_APDU_FLAGS_CHAINING; apdu.data = part; apdu.datalen = apdu.lc = plen; /* If the last part has odd length, we add zero padding to make it even. * Gnuk does not allow data with odd length */ if (plen < 256 && (plen % 2) != 0) { memcpy(roundbuf, part, plen); roundbuf[plen++] = 0; apdu.data = roundbuf; apdu.datalen = apdu.lc = plen; } 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), "UPDATE BINARY returned error"); } LOG_FUNC_RETURN(card->ctx, (int)length); } /** * Internal: use PUT DATA command to write. */ static int pgp_put_data_plain(sc_card_t *card, unsigned int tag, const u8 *buf, size_t buf_len) { struct pgp_priv_data *priv = DRVDATA(card); sc_apdu_t apdu; u8 ins = 0xDA; u8 p1 = tag >> 8; u8 p2 = tag & 0xFF; u8 apdu_case = (card->type == SC_CARD_TYPE_OPENPGP_GNUK) ? SC_APDU_CASE_3_SHORT : SC_APDU_CASE_3; int r; LOG_FUNC_CALLED(card->ctx); /* Extended Header list (DO 004D) needs a variant of PUT DATA command */ if (tag == 0x004D) { ins = 0xDB; p1 = 0x3F; p2 = 0xFF; } /* build APDU */ if (buf != NULL && buf_len > 0) { sc_format_apdu(card, &apdu, apdu_case, ins, p1, p2); /* if card/reader does not support extended APDUs, but chaining, then set it */ if (((card->caps & SC_CARD_CAP_APDU_EXT) == 0) && (priv->ext_caps & EXT_CAP_CHAINING)) apdu.flags |= SC_APDU_FLAGS_CHAINING; apdu.data = (u8 *)buf; apdu.datalen = buf_len; apdu.lc = buf_len; } else { /* This case is to empty DO */ sc_format_apdu(card, &apdu, SC_APDU_CASE_1, ins, p1, p2); } /* send APDU to card */ r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); /* check response */ r = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(card->ctx, r, "Card returned error"); LOG_FUNC_RETURN(card->ctx, (int)buf_len); } /** * 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; pgp_do_info_t *dinfo = NULL; int r; LOG_FUNC_CALLED(card->ctx); /* 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) dinfo = pgp_get_info_by_tag(card, tag); 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); } else if ((dinfo->access & WRITE_MASK) == WRITE_NEVER) { sc_log(card->ctx, "DO %04X is not writable.", tag); LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_ALLOWED); } /* Check data size. * We won't check other DOs than 7F21 (certificate), because their capacity * is hard-coded and may change in various version of the card. * If we check here, the driver may be stuck to a limit version number of card. * 7F21 size is soft-coded, so we can check it. */ if (tag == DO_CERT && buf_len > priv->max_cert_size) { sc_log(card->ctx, "Data size %"SC_FORMAT_LEN_SIZE_T"u exceeds DO size limit %"SC_FORMAT_LEN_SIZE_T"u.", buf_len, priv->max_cert_size); LOG_FUNC_RETURN(card->ctx, SC_ERROR_WRONG_LENGTH); } if (tag == DO_CERT && card->type == SC_CARD_TYPE_OPENPGP_GNUK) { /* Gnuk need a special way to write certificate. */ r = gnuk_write_certificate(card, buf, buf_len); } else { r = pgp_put_data_plain(card, tag, buf, buf_len); } /* 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."); } LOG_TEST_RET(card->ctx, r, "PUT DATA returned error"); if (affected_blob) { /* update the corresponding file */ sc_log(card->ctx, "Updating the corresponding blob data"); r = pgp_set_blob(affected_blob, buf, buf_len); if (r < 0) sc_log(card->ctx, "Failed to update blob %04X. Error %d.", affected_blob->id, r); /* pgp_set_blob()'s failures do not impact pgp_put_data()'s result */ } LOG_FUNC_RETURN(card->ctx, (int)buf_len); } /** * 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) { struct pgp_priv_data *priv = DRVDATA(card); LOG_FUNC_CALLED(card->ctx); if (data->pin_type != SC_AC_CHV) LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "invalid PIN type"); /* In general, the PIN Reference is extracted from the key-id, * for example, CHV0 -> Ref=0, CHV1 -> Ref=1. * 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. * In v2, they are named PW1, PW3 (PW1 operates in 2 modes). * * The PIN references (P2 in APDU) for "VERIFY" are the same in both versions: * 81 (CHV1 or PW1), 82 (CHV2 or PW1-mode 2), 83 (CHV3 or PW3), * On the other hand from version 2.0 "CHANGE REFERENCE DATA" and * "RESET RETRY COUNTER" don't support PW1-mode 2 (82) and need this * value changed to PW1 (81). * Both of these commands also differ between card versions in that * v1 cards can use only implicit old PIN or CHV3 test for both commands * whereas v2 can use both implicit (for PW3) and explicit * (for special "Resetting Code") PIN test for "RESET RETRY COUNTER" * and only explicit test for "CHANGE REFERENCE DATA". * * Note that if this function is called from sc_pkcs15_verify_pin() in pkcs15-pin.c, * the Ref is already 81, 82, 83. */ /* convert the PIN Reference if needed */ data->pin_reference |= 0x80; /* check version-dependent constraints */ if (data->cmd == SC_PIN_CMD_CHANGE || data->cmd == SC_PIN_CMD_UNBLOCK) { if (priv->bcd_version >= OPENPGP_CARD_2_0) { if (data->pin_reference == 0x82) data->pin_reference = 0x81; if (data->cmd == SC_PIN_CMD_CHANGE) { 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"); data->flags &= ~SC_PIN_CMD_IMPLICIT_CHANGE; } } else { if (data->pin1.len != 0) { sc_log(card->ctx, "v1 cards don't support explicit old or CHV3 PIN, PIN ignored."); sc_log(card->ctx, "please make sure that you have verified the relevant PIN first."); data->pin1.len = 0; } data->flags |= SC_PIN_CMD_IMPLICIT_CHANGE; } } 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"); /* ensure pin_reference is 81, 82, 83 */ if (data->pin_reference < 0x81 || data->pin_reference > 0x83) LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "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"); data->pin1.tries_left = c4data[4 + (data->pin_reference & 0x0F)]; data->pin1.max_tries = 3; data->pin1.logged_in = SC_PIN_STATE_UNKNOWN; if (tries_left != NULL) *tries_left = data->pin1.tries_left; LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } LOG_FUNC_RETURN(card->ctx, iso_ops->pin_cmd(card, data, tries_left)); } /** * 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, const sc_security_env_t *env, int se_num) { struct pgp_priv_data *priv = DRVDATA(card); LOG_FUNC_CALLED(card->ctx); /* 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"); if (!(env->flags & SC_SEC_ENV_KEY_REF_PRESENT) || (env->key_ref_len != 1)) LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "exactly one key reference required"); if (env->flags & SC_SEC_ENV_FILE_REF_PRESENT) LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "passing file references not supported"); sc_log(card->ctx, "Key ref %d", env->key_ref[0]); switch (env->operation) { case SC_SEC_OPERATION_SIGN: sc_log(card->ctx, "Operation: Sign."); if (env->key_ref[0] != 0x00 && env->key_ref[0] != 0x02) { LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED, "Key reference not compatible with " "requested usage"); } break; case SC_SEC_OPERATION_DECIPHER: sc_log(card->ctx, "Operation: Decipher."); /* we allow key ref 2 (auth key) to be used for deciphering */ if (env->key_ref[0] != 0x01 && env->key_ref[0] != 0x02) { LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED, "Key reference not compatible with " "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"); } priv->sec_env = *env; LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } /** * 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, size_t data_len, u8 * out, size_t outlen) { struct pgp_priv_data *priv = DRVDATA(card); sc_security_env_t *env = &priv->sec_env; sc_apdu_t apdu; u8 apdu_case = (card->type == SC_CARD_TYPE_OPENPGP_GNUK) ? SC_APDU_CASE_4_SHORT : SC_APDU_CASE_4; int r; LOG_FUNC_CALLED(card->ctx); if (env->operation != SC_SEC_OPERATION_SIGN) LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "invalid operation"); switch (env->key_ref[0]) { case 0x00: /* signature key */ /* PSO SIGNATURE */ sc_format_apdu(card, &apdu, apdu_case, 0x2A, 0x9E, 0x9A); break; case 0x02: /* authentication key */ /* INTERNAL AUTHENTICATE */ sc_format_apdu(card, &apdu, apdu_case, 0x88, 0, 0); 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"); } /* if card/reader does not support extended APDUs, but chaining, then set it */ if (((card->caps & SC_CARD_CAP_APDU_EXT) == 0) && (priv->ext_caps & EXT_CAP_CHAINING)) apdu.flags |= SC_APDU_FLAGS_CHAINING; apdu.lc = data_len; apdu.data = (u8 *)data; apdu.datalen = data_len; apdu.le = ((outlen >= 256) && !(card->caps & SC_CARD_CAP_APDU_EXT)) ? 256 : outlen; apdu.resp = out; apdu.resplen = outlen; 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, (int)apdu.resplen); } /** * ABI: ISO 7816-8 DECIPHER - perform deciphering operation. */ static int pgp_decipher(sc_card_t *card, const u8 *in, size_t inlen, u8 *out, size_t outlen) { struct pgp_priv_data *priv = DRVDATA(card); sc_security_env_t *env = &priv->sec_env; sc_apdu_t apdu; u8 apdu_case = SC_APDU_CASE_4; u8 *temp = NULL, *p = NULL; size_t templen, pklen, dolen; int r; LOG_FUNC_CALLED(card->ctx); /* 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); /* 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; 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"); } switch (env->key_ref[0]) { case 0x01: /* Decryption key */ case 0x02: /* authentication key */ /* PSO DECIPHER */ sc_format_apdu(card, &apdu, apdu_case, 0x2A, 0x80, 0x86); break; case 0x00: /* signature key */ default: free(temp); LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "invalid key reference"); } /* Gnuk only supports short APDU, so we need to use command chaining */ if (card->type == SC_CARD_TYPE_OPENPGP_GNUK) { apdu.flags |= SC_APDU_FLAGS_CHAINING; } /* if card/reader does not support extended APDUs, but chaining, then set it */ if (((card->caps & SC_CARD_CAP_APDU_EXT) == 0) && (priv->ext_caps & EXT_CAP_CHAINING)) apdu.flags |= SC_APDU_FLAGS_CHAINING; apdu.lc = inlen; apdu.data = (u8 *)in; apdu.datalen = inlen; apdu.le = ((outlen >= 256) && !(card->caps & SC_CARD_CAP_APDU_EXT)) ? 256 : outlen; 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"); 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); } #ifdef ENABLE_OPENSSL /** * 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) { struct pgp_priv_data *priv = DRVDATA(card); pgp_blob_t *algo_blob; const unsigned int tag = 0x00C0 | key_info->key_id; u8 *data; size_t data_len; int r = SC_SUCCESS; unsigned int i; LOG_FUNC_CALLED(card->ctx); r = pgp_seek_blob(card, priv->mf, tag, &algo_blob); LOG_TEST_RET(card->ctx, r, "Cannot get old algorithm attributes"); 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){ data_len = key_info->u.ec.oid_len+1; data = malloc(data_len); if (!data) LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_ENOUGH_MEMORY); data[0] = key_info->algorithm; /* oid.value is type int, therefore we need to loop over the values */ for (i=0; i < key_info->u.ec.oid_len; i++){ data[i+1] = key_info->u.ec.oid.value[i]; } } /* 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); data_len = 6; data = malloc(data_len); if (!data) LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_ENOUGH_MEMORY); data[0] = key_info->algorithm; ushort2bebytes(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(data+3, SC_OPENPGP_MAX_EXP_BITS); /* Import-Format of private key (e,p,q) */ data[5] = SC_OPENPGP_KEYFORMAT_RSA_STD; } else { sc_log(card->ctx, "Unknown algorithm id"); LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } pgp_set_blob(algo_blob, data, data_len); free(data); r = pgp_put_data(card, tag, algo_blob->data, data_len); /* Note: Don't use pgp_set_blob to set data, because it won't touch the real DO */ LOG_TEST_RET(card->ctx, r, "Cannot set new algorithm attributes"); } else { sc_cardctl_openpgp_keygen_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); } /** * Internal: store creation time of key. * Pass non-zero outtime to use predefined time. * Pass zero/null outtime to calculate current time. outtime then will be output. * Pass null outtime to not receive output. **/ static int pgp_store_creationtime(sc_card_t *card, u8 key_id, time_t *outtime) { int r; time_t createtime = 0; const size_t timestrlen = 64; char timestring[65]; u8 buf[4]; struct tm tm; LOG_FUNC_CALLED(card->ctx); 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; else if (outtime != NULL) /* set output */ *outtime = createtime = time(NULL); #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); r = pgp_put_data(card, 0x00CD + key_id, buf, 4); LOG_TEST_RET(card->ctx, r, "Cannot write to DO"); LOG_FUNC_RETURN(card->ctx, r); } /** * Internal: calculate and store PGP fingerprints. * Reference: GnuPG, app-openpgp.c. **/ static int pgp_calculate_and_store_fingerprint(sc_card_t *card, time_t ctime, sc_cardctl_openpgp_keygen_info_t *key_info) { u8 fingerprint[SHA_DIGEST_LENGTH]; 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 = 0x00C6 + key_info->key_id; pgp_blob_t *fpseq_blob = NULL; u8 *newdata = NULL; int r; LOG_FUNC_CALLED(card->ctx); /* 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 || key_info->algorithm == SC_OPENPGP_KEYALGO_ECDSA) { if (key_info->u.ec.ecpoint == NULL || (key_info->u.ec.ecpoint_len) == 0) { sc_log(card->ctx, "Error: ecpoint required!"); LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } /* 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.oid_len) /* oid */ + (key_info->u.ec.ecpoint_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(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 */ p += 2; *p = 4; /* Version 4 key */ ulong2bebytes(++p, (unsigned long)ctime); /* Creation time */ p += 4; /* 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 || 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.oid_len; p += 1; memcpy(p, key_info->u.ec.oid.value, key_info->u.ec.oid_len); p += key_info->u.ec.oid_len; memcpy(p, key_info->u.ec.ecpoint, key_info->u.ec.ecpoint_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 += key_info->u.ec.ecpoint_len; *p = 0x03; /* number of bytes following */ p += 1; *p = 0x01; /* version of this format */ p += 1; if (key_info->u.ec.ecpoint_len <= 256){ /* ec bit size <= 256 */ *p = 0x08; /* KDF algo */ *(p+1) = 0x07; /* KEK algo */ } else if (key_info->u.ec.ecpoint_len <= 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 */ SHA1(fp_buffer, fp_buffer_len, fingerprint); free(fp_buffer); /* store 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, "Updating fingerprint blob 00C5."); fpseq_blob = pgp_find_blob(card, 0x00C5); 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 == 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->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); err: LOG_FUNC_RETURN(card->ctx, r); } /** * Internal: update pubkey blob. * Note that modulus_len, exponent_len is measured in bit. **/ static int pgp_update_pubkey_blob(sc_card_t *card, sc_cardctl_openpgp_keygen_info_t *key_info) { struct pgp_priv_data *priv = DRVDATA(card); pgp_blob_t *pk_blob; 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_info->key_id == SC_OPENPGP_KEY_SIGN) blob_id = DO_SIGN_SYM; else if (key_info->key_id == SC_OPENPGP_KEY_ENCR) blob_id = DO_ENCR_SYM; else if (key_info->key_id == SC_OPENPGP_KEY_AUTH) blob_id = DO_AUTH_SYM; else { LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "Invalid key ID; must be 1, 2, or 3"); } 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"); /* encode pubkey */ /* 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 || key_info->algorithm == SC_OPENPGP_KEYALGO_ECDSA){ memset(&p15pubkey, 0, sizeof(p15pubkey)); p15pubkey.algorithm = SC_ALGORITHM_EC; p15pubkey.u.ec.ecpointQ.value = key_info->u.ec.ecpoint; p15pubkey.u.ec.ecpointQ.len = key_info->u.ec.ecpoint_len; } else LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); r = sc_pkcs15_encode_pubkey(card->ctx, &p15pubkey, &data, &len); LOG_TEST_RET(card->ctx, r, "Cannot encode pubkey"); sc_log(card->ctx, "Updating blob %04X's content.", blob_id); r = pgp_set_blob(pk_blob, data, len); free(data); LOG_TEST_RET(card->ctx, r, "Cannot update blob content"); LOG_FUNC_RETURN(card->ctx, r); } /** * 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) { time_t ctime = 0; u8 *in = data; int r; LOG_FUNC_CALLED(card->ctx); /* store creation time */ r = pgp_store_creationtime(card, key_info->key_id, &ctime); LOG_TEST_RET(card->ctx, r, "Cannot store creation time"); /* parse response. Ref: pgp_enumerate_blob() */ while (data_len > (size_t) (in - data)) { unsigned int cla, tag, tmptag; size_t len; u8 *part = in; /* parse TLV structure */ 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) { 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); } /* 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 public key */ else if (tag == 0x0086) { if (key_info->algorithm != SC_OPENPGP_KEYALGO_ECDSA && key_info->algorithm != SC_OPENPGP_KEYALGO_ECDH) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); } /* set the output data */ /* len is ecpoint length + format byte * see section 7.2.14 of 3.3.1 specs */ if ((key_info->u.ec.ecpoint_len) != (len - 1) || key_info->u.ec.ecpoint == NULL) { free(key_info->u.ec.ecpoint); key_info->u.ec.ecpoint = malloc(len); if (key_info->u.ec.ecpoint == NULL) LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_ENOUGH_MEMORY); } memcpy(key_info->u.ec.ecpoint, part + 1, len - 1); key_info->u.ec.ecpoint_len = len - 1; } /* go to next part to parse */ /* This will be different from pgp_enumerate_blob() a bit */ in = part + ((tag != 0x7F49) ? len : 0); } /* calculate and store fingerprint */ sc_log(card->ctx, "Calculate and store fingerprint"); r = pgp_calculate_and_store_fingerprint(card, ctime, key_info); 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); LOG_FUNC_RETURN(card->ctx, r); } /** * Internal: update card->algorithms */ static int pgp_update_card_algorithms(sc_card_t *card, sc_cardctl_openpgp_keygen_info_t *key_info) { sc_algorithm_info_t *algo; 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) 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.", (unsigned int)id); LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } /* get the algorithm corresponding to the key ID */ algo = card->algorithms + (id - 1); /* 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 || key_info->algorithm == SC_OPENPGP_KEYALGO_ECDSA) { algo->algorithm = SC_ALGORITHM_EC; algo->key_length = (unsigned int)((key_info->u.ec.ecpoint_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 **/ static int pgp_gen_key(sc_card_t *card, sc_cardctl_openpgp_keygen_info_t *key_info) { sc_apdu_t apdu; /* temporary variables to hold APDU params */ u8 apdu_case; 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); /* protect incompatible cards against non-RSA */ if (key_info->algorithm != SC_OPENPGP_KEYALGO_RSA && priv->bcd_version < OPENPGP_CARD_3_0) LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); if (key_info->algorithm == SC_OPENPGP_KEYALGO_EDDSA && 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->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 { LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "Invalid key ID; must be 1, 2, or 3"); } /* set attributes for new-generated key */ r = pgp_update_new_algo_attr(card, key_info); LOG_TEST_RET(card->ctx, r, "Cannot set attributes for new-generated key"); /* Test whether we will need extended APDU. 1900 is an * 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->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() */ apdu_le = card->max_recv_size; apdu_case = SC_APDU_CASE_4_EXT; } else { apdu_case = SC_APDU_CASE_4_SHORT; apdu_le = 256; resplen = MAXLEN_RESP_PUBKEY; } if (card->type == SC_CARD_TYPE_OPENPGP_GNUK) { resplen = MAXLEN_RESP_PUBKEY_GNUK; } /* prepare APDU */ sc_format_apdu(card, &apdu, apdu_case, 0x47, 0x80, 0); apdu.data = apdu_data; 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(1, apdu.resplen); if (apdu.resp == NULL) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_ENOUGH_MEMORY); } /* send */ 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."); 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 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); err: free(apdu.resp); LOG_FUNC_RETURN(card->ctx, r); } /** * Internal: build TLV. * * FIXME use `sc_asn1_put_tag` or similar instead * * @param[in] data The data ("value") part to build TLV. * @param[in] len Data length * @param[out] out The buffer of overall TLV. This buffer should be freed later. * @param[out] outlen The length of buffer out. **/ static int pgp_build_tlv(sc_context_t *ctx, unsigned int tag, u8 *data, size_t len, u8 **out, size_t *outlen) { u8 highest_order = 0; int r; r = sc_asn1_write_element(ctx, tag, data, len, out, outlen); LOG_TEST_RET(ctx, r, "Failed to write ASN.1 element"); /* Restore class bits stripped by sc_asn1_write_element */ /* determine the leftmost byte of tag, which contains class bits */ while ((tag >> 8*highest_order) != 0) { highest_order++; } if (highest_order != 0) highest_order--; /* restore class bits in output */ if (highest_order < 4) *out[0] |= (tag >> 8*highest_order); return SC_SUCCESS; } /** * Internal: set Tag & Length components for TLV, store them in buffer. * * FIXME use `sc_asn1_put_tag` or similar instead * * Return the total length of Tag + Length. * Note that the Value components is not counted. * Ref: add_tlv() of GnuPG code. **/ static size_t 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; if (length < 128) *p++ = (u8)length; else if (length < 256) { *p++ = 0x81; *p++ = (u8)length; } else { if (length > 0xffff) length = 0xffff; *p++ = 0x82; *p++ = (length >> 8) & 0xFF; *p++ = length & 0xFF; } return p - buffer; } /** * 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) { sc_context_t *ctx = card->ctx; /* Cardholder private key template (7F48) part */ const size_t max_prtem_len = 7*(1 + 3); /* 7 components */ /* 1 for tag name (91, 92... 97) * 3 for storing length */ u8 pritemplate[7*(1 + 3)]; size_t tpl_len = 0; /* Actual size of pritemplate */ /* Concatenation of key data */ u8 kdata[3 + 256 + 256 + 512]; /* Exponent is stored in 3 bytes * With maximum 4096-bit key, * p and q can be stored in 256 bytes (2048 bits). * Maximum 4096-bit modulus is stored in 512 bytes */ size_t kdata_len = 0; /* Actual size of kdata */ u8 *tlvblock = NULL; size_t tlvlen = 0; u8 *tlv_5f48 = NULL; size_t tlvlen_5f48 = 0; u8 *tlv_7f48 = NULL; size_t tlvlen_7f48 = 0; u8 *data = NULL; size_t len = 0; u8 *p = NULL; 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); /* RSA */ if (key_info->algorithm == SC_OPENPGP_KEYALGO_RSA){ components[0] = key_info->u.rsa.e; components[1] = key_info->u.rsa.p; components[2] = key_info->u.rsa.q; componentlens[0] = key_info->u.rsa.e_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.e_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.n; componentlens[3] = key_info->u.rsa.n_len; componenttags[3] = 0x97; componentnames[3] = "modulus"; comp_to_add = 4; } /* validate */ if (comp_to_add == 4 && (key_info->u.rsa.n == NULL || key_info->u.rsa.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); /* maximum 32 bit exponent length allowed on OpenPGP Card */ assert(key_info->u.rsa.e_len <= SC_OPENPGP_MAX_EXP_BITS); /* 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.e_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.e, e_len_bytes); /* set key_info->u.rsa.e to new buffer */ free(key_info->u.rsa.e); key_info->u.rsa.e = p; components[0] = p; key_info->u.rsa.e_len = SC_OPENPGP_MAX_EXP_BITS; /* we store info in bits */ componentlens[0] = max_e_len_bytes; /* ... but in bytes for header list */ } } /* ECC */ else if (key_info->algorithm == SC_OPENPGP_KEYALGO_ECDH || key_info->algorithm == SC_OPENPGP_KEYALGO_ECDSA){ 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); } /* 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]); len = set_taglength_tlv(p, componenttags[i], componentlens[i]); tpl_len += len; /* * <-- kdata_len --><-- Copy here --> * kdata |===============|___________________ */ memcpy(kdata + kdata_len, components[i], componentlens[i]); kdata_len += componentlens[i]; /* Move p to next part and build */ p += len; } /* TODO: Components for CRT format */ /* 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"); tlv_7f48[0] |= 0x7F; r = pgp_build_tlv(ctx, 0x5f48, kdata, kdata_len, &tlv_5f48, &tlvlen_5f48); 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(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); LOG_TEST_GOTO_ERR(ctx, r, "Cannot build TLV for Extended Header list"); /* set output */ if (result != NULL) { *result = tlvblock; *resultlen = tlvlen; } else { free(tlvblock); } err: free(data); free(tlv_5f48); free(tlv_7f48); LOG_FUNC_RETURN(ctx, r); } /** * ABI (card ctl): store key **/ static int pgp_store_key(sc_card_t *card, sc_cardctl_openpgp_keystore_info_t *key_info) { sc_cardctl_openpgp_keygen_info_t pubkey; u8 *data = NULL; size_t len = 0; int r; 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) 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 */ /* RSA */ if (key_info->algorithm == SC_OPENPGP_KEYALGO_RSA){ /* we just support standard key format */ 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.e_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.e_len); LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } memset(&pubkey, 0, sizeof(pubkey)); pubkey.key_id = key_info->key_id; pubkey.algorithm = key_info->algorithm; if (key_info->u.rsa.n && key_info->u.rsa.n_len && key_info->u.rsa.e && key_info->u.rsa.e_len) { pubkey.u.rsa.modulus = key_info->u.rsa.n; pubkey.u.rsa.modulus_len = key_info->u.rsa.n_len; pubkey.u.rsa.exponent = key_info->u.rsa.e; pubkey.u.rsa.exponent_len = key_info->u.rsa.e_len; } else LOG_FUNC_RETURN(card->ctx,SC_ERROR_INVALID_ARGUMENTS); } /* ECC */ else if (key_info->algorithm == SC_OPENPGP_KEYALGO_ECDH || key_info->algorithm == SC_OPENPGP_KEYALGO_ECDSA){ memset(&pubkey, 0, sizeof(pubkey)); pubkey.key_id = key_info->key_id; pubkey.algorithm = key_info->algorithm; if (key_info->u.ec.ecpointQ && key_info->u.ec.ecpointQ_len){ pubkey.u.ec.ecpoint = key_info->u.ec.ecpointQ; pubkey.u.ec.ecpoint_len = key_info->u.ec.ecpointQ_len; pubkey.u.ec.oid = key_info->u.ec.oid; pubkey.u.ec.oid_len = key_info->u.ec.oid_len; } else LOG_FUNC_RETURN(card->ctx,SC_ERROR_INVALID_ARGUMENTS); } else LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); 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); LOG_TEST_GOTO_ERR(card->ctx, r, "Failed to build Extended Header list"); /* write to DO */ r = pgp_put_data(card, 0x4D, data, len); LOG_TEST_GOTO_ERR(card->ctx, r, "Failed to write to DO 004D"); /* 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"); /* calculate and store fingerprint */ sc_log(card->ctx, "Calculate and store fingerprint"); r = pgp_calculate_and_store_fingerprint(card, key_info->creationtime, &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, &pubkey); sc_log(card->ctx, "Update card algorithms"); pgp_update_card_algorithms(card, &pubkey); err: free(data); LOG_FUNC_RETURN(card->ctx, r); } #endif /* ENABLE_OPENSSL */ /** * ABI (card ctl): erase card **/ static int pgp_erase_card(sc_card_t *card) { /* Special series of commands to erase OpenPGP 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. */ 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", "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", /* block PIN3 */ "00:20:00:83:08:40:40:40:40:40:40:40:40", "00:20:00:83:08:40:40:40:40:40:40:40:40", "00:20:00:83:08:40:40:40:40:40:40:40:40", "00:20:00:83:08:40:40:40:40:40:40:40:40", /* TERMINATE */ "00:e6: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); 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"); } 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"); } 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; pgp_free_blobs(priv->mf); priv->mf = new_mf; populate_blobs_to_mf(card, priv); } LOG_FUNC_RETURN(card->ctx, r); } /** * 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) { int r; LOG_FUNC_CALLED(card->ctx); switch(cmd) { case SC_CARDCTL_GET_SERIALNR: 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); 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); LOG_FUNC_RETURN(card->ctx, r); break; #endif /* ENABLE_OPENSSL */ case SC_CARDCTL_ERASE_CARD: r = pgp_erase_card(card); LOG_FUNC_RETURN(card->ctx, r); break; } LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } /** * 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; u8 data[4] = { 0x4D, 0x02, 0x00, 0x00 }; LOG_FUNC_CALLED(ctx); 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"); r = pgp_put_data(card, 0xC6 + key_id, NULL, 0); LOG_TEST_RET(ctx, r, "Failed to delete fingerprints"); /* delete creation time */ sc_log(ctx, "Delete creation time"); r = pgp_put_data(card, 0xCD + key_id, NULL, 0); LOG_TEST_RET(ctx, r, "Failed to delete creation time"); /* rewrite Extended Header List */ sc_log(ctx, "Rewrite Extended Header List"); 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, data, sizeof(data)); LOG_FUNC_RETURN(ctx, r); } /** * 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) { struct pgp_priv_data *priv = DRVDATA(card); pgp_blob_t *blob; sc_file_t *file; int r; LOG_FUNC_CALLED(card->ctx); /* 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"); /* save "current" blob */ blob = priv->current; /* 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)) { /* These tags are just symbolic. We don't really delete them. */ r = SC_SUCCESS; } else if (card->type == SC_CARD_TYPE_OPENPGP_GNUK && file->id == DO_SIGN_SYM) { r = gnuk_delete_key(card, 1); } else if (card->type == SC_CARD_TYPE_OPENPGP_GNUK && file->id == DO_ENCR_SYM) { r = gnuk_delete_key(card, 2); } else if (card->type == SC_CARD_TYPE_OPENPGP_GNUK && file->id == DO_AUTH_SYM) { r = gnuk_delete_key(card, 3); } else { /* 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; LOG_FUNC_RETURN(card->ctx, r); } /** * ABI: ISO 7816-4 UPDATE BINARY - update data in current EF. */ static int pgp_update_binary(sc_card_t *card, unsigned int idx, const u8 *buf, size_t count, unsigned long flags) { struct pgp_priv_data *priv = DRVDATA(card); pgp_blob_t *blob = priv->current; int r = SC_SUCCESS; LOG_FUNC_CALLED(card->ctx); /* We will use PUT DATA to write to DO. * As PUT DATA does not support idx, we don't either */ if (idx > 0) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INCORRECT_PARAMETERS); /* When a dummy file, e.g "11001101", is selected, the current blob * is set to NULL. We don't really put data to dummy file. */ if (blob != NULL) { r = pgp_put_data(card, blob->id, buf, count); } LOG_FUNC_RETURN(card->ctx, r); } /** * 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. */ struct sc_card_driver * sc_get_openpgp_driver(void) { struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); iso_ops = iso_drv->ops; pgp_ops = *iso_ops; pgp_ops.match_card = pgp_match_card; pgp_ops.init = pgp_init; 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 = 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; pgp_ops.compute_signature= pgp_compute_signature; pgp_ops.decipher = pgp_decipher; 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; } OpenSC-0.26.1/src/libopensc/card-openpgp.h000066400000000000000000000136751474147347300202560ustar00rootroot00000000000000/* * 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; typedef enum _pgp_ext_caps { /* extended capabilities/features: bit flags */ 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; 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; /* 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 /* 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 typedef struct _pgp_ec_curves { struct sc_object_id oid; size_t size; } pgp_ec_curves_t; #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 */ 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 */ 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; }; #define BCD2UCHAR(x) (((((x) & 0xF0) >> 4) * 10) + ((x) & 0x0F)) #endif OpenSC-0.26.1/src/libopensc/card-piv.c000066400000000000000000005773061474147347300174050ustar00rootroot00000000000000/* * card-piv.c: Support for PIV-II from NIST SP800-73 * card-default.c: Support for cards with no driver * * Copyright (C) 2001, 2002 Juha Yrjölä * Copyright (C) 2005-2023 Douglas E. Engert * Copyright (C) 2006, Identity Alliance, Thomas Harning * Copyright (C) 2007, EMC, Russell Larner * * 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 /* openssl needed for card administration and SM */ #include #include #include #include #if !defined(OPENSSL_NO_EC) #include #endif #endif /* 800-73-4 SM and VCI need: ECC, SM and real OpenSSL >= 1.1 */ #if defined(ENABLE_OPENSSL) && defined(ENABLE_SM) && !defined(OPENSSL_NO_EC) && !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000L #else #undef ENABLE_PIV_SM #endif #ifdef ENABLE_PIV_SM #include #include "compression.h" #endif #include "internal.h" #include "asn1.h" #include "cardctl.h" #include "simpletlv.h" enum { PIV_OBJ_CCC = 0, PIV_OBJ_CHUI, /* PIV_OBJ_UCHUI is not in new with 800-73-2 */ PIV_OBJ_X509_PIV_AUTH, PIV_OBJ_CHF, PIV_OBJ_PI, PIV_OBJ_CHFI, PIV_OBJ_X509_DS, PIV_OBJ_X509_KM, PIV_OBJ_X509_CARD_AUTH, PIV_OBJ_SEC_OBJ, PIV_OBJ_DISCOVERY, PIV_OBJ_HISTORY, PIV_OBJ_RETIRED_X509_1, PIV_OBJ_RETIRED_X509_2, PIV_OBJ_RETIRED_X509_3, PIV_OBJ_RETIRED_X509_4, PIV_OBJ_RETIRED_X509_5, PIV_OBJ_RETIRED_X509_6, PIV_OBJ_RETIRED_X509_7, PIV_OBJ_RETIRED_X509_8, PIV_OBJ_RETIRED_X509_9, PIV_OBJ_RETIRED_X509_10, PIV_OBJ_RETIRED_X509_11, PIV_OBJ_RETIRED_X509_12, PIV_OBJ_RETIRED_X509_13, PIV_OBJ_RETIRED_X509_14, PIV_OBJ_RETIRED_X509_15, PIV_OBJ_RETIRED_X509_16, PIV_OBJ_RETIRED_X509_17, PIV_OBJ_RETIRED_X509_18, 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, PIV_OBJ_9D06, PIV_OBJ_9E06, PIV_OBJ_8206, PIV_OBJ_8306, PIV_OBJ_8406, PIV_OBJ_8506, PIV_OBJ_8606, PIV_OBJ_8706, PIV_OBJ_8806, PIV_OBJ_8906, PIV_OBJ_8A06, PIV_OBJ_8B06, PIV_OBJ_8C06, PIV_OBJ_8D06, PIV_OBJ_8E06, PIV_OBJ_8F06, PIV_OBJ_9006, PIV_OBJ_9106, PIV_OBJ_9206, PIV_OBJ_9306, PIV_OBJ_9406, PIV_OBJ_9506, PIV_OBJ_LAST_ENUM }; /* * Flags in the piv_obj_cache: * 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. * 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 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_COMPRESSED 2 #define PIV_OBJ_CACHE_NOT_PRESENT 8 typedef struct piv_obj_cache { u8* obj_data; size_t obj_len; u8* internal_obj_data; /* like a cert in the object */ size_t internal_obj_len; 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; // clang-fromat off #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"} }; // clang-format on /* 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 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 { struct sc_lv_data aid_der; /* previous aid response to compare */ int enumtag; 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 */ 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 */ 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 */ piv_obj_cache_t obj_cache[PIV_OBJ_LAST_ENUM]; int keysWithOnCardCerts; int keysWithOffCardCerts; char * offCardCertURL; int pin_preference; /* set from Discovery object */ int logged_in; int pstate; int pin_cmd_verify; 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 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 length without version */ size_t len_long; /* With version and other stuff */ u8 *value; }; /* * 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. * Discovery object was added. * * 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. */ /* ATRs of cards known to have PIV applet. But must still be tested for a PIV applet */ 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 }, /* IDEMIA (new name for Oberthur) (DoD Alternate Token IDEMIA Cosmo V8.0 2019*/ { "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 }, /* 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; } ec_curves[] = { {{{1, 2, 840, 10045, 3, 1, 7, -1}}, 256}, /* secp256r1, nistp256, prime256v1, ansiX9p256r1 */ {{{1, 3, 132, 0, 34, -1}}, 384}, /* secp384r1, nistp384, prime384v1, ansiX9p384r1 */ {{{-1}}, 0} /* This entry must not be touched. */ }; /* all have same AID */ static struct piv_aid piv_aids[] = { {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" }, {0, 9, 0, NULL } }; /* card_issues - bugs in PIV implementations requires special handling */ #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_EC 0x00040000U /* No EC at all */ /* * Flags in the piv_object: * PIV_OBJECT_NOT_PRESENT: the presents of the object is * indicated by the History object. */ #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]; u8 containerid[2]; /* will use as relative paths for simulation */ int flags; /* object has some internal object like a cert */ }; /* Must be in order, and one per enumerated PIV_OBJ */ // clang-format off static const struct piv_object piv_objects[] = { { PIV_OBJ_CCC, "Card Capability Container", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.1.219.0", 3, "\x5F\xC1\x07", "\xDB\x00", PIV_OBJECT_NEEDS_VCI}, { PIV_OBJ_CHUI, "Card Holder Unique Identifier", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.48.0", 3, "\x5F\xC1\x02", "\x30\x00", 0}, { PIV_OBJ_X509_PIV_AUTH, "X.509 Certificate for PIV Authentication", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.1.1", 3, "\x5F\xC1\x05", "\x01\x01", PIV_OBJECT_TYPE_CERT | PIV_OBJECT_NEEDS_VCI} , { PIV_OBJ_CHF, "Card Holder Fingerprints", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.96.16", 3, "\x5F\xC1\x03", "\x60\x10", PIV_OBJECT_NEEDS_PIN | PIV_OBJECT_NEEDS_VCI}, { PIV_OBJ_PI, "Printed Information", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.48.1", 3, "\x5F\xC1\x09", "\x30\x01", PIV_OBJECT_NEEDS_PIN | PIV_OBJECT_NEEDS_VCI}, { PIV_OBJ_CHFI, "Cardholder Facial Images", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.96.48", 3, "\x5F\xC1\x08", "\x60\x30", PIV_OBJECT_NEEDS_PIN | PIV_OBJECT_NEEDS_VCI}, { PIV_OBJ_X509_DS, "X.509 Certificate for Digital Signature", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.1.0", 3, "\x5F\xC1\x0A", "\x01\x00", PIV_OBJECT_TYPE_CERT | PIV_OBJECT_NEEDS_VCI}, { PIV_OBJ_X509_KM, "X.509 Certificate for Key Management", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.1.2", 3, "\x5F\xC1\x0B", "\x01\x02", PIV_OBJECT_TYPE_CERT}, { PIV_OBJ_X509_CARD_AUTH, "X.509 Certificate for Card Authentication", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.5.0", 3, "\x5F\xC1\x01", "\x05\x00", PIV_OBJECT_TYPE_CERT}, { PIV_OBJ_SEC_OBJ, "Security Object", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.144.0", 3, "\x5F\xC1\x06", "\x90\x00", PIV_OBJECT_NEEDS_VCI}, { PIV_OBJ_DISCOVERY, "Discovery Object", SC_ASN1_APP | SC_ASN1_CONS | 0x1E, "2.16.840.1.101.3.7.2.96.80", 1, "\x7E", "\x60\x50", 0}, { PIV_OBJ_HISTORY, "Key History Object", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.96.96", 3, "\x5F\xC1\x0C", "\x60\x60", 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", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.16.1", 3, "\x5F\xC1\x0D", "\x10\x01", 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", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.16.2", 3, "\x5F\xC1\x0E", "\x10\x02", 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", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.16.3", 3, "\x5F\xC1\x0F", "\x10\x03", 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", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.16.4", 3, "\x5F\xC1\x10", "\x10\x04", 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", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.16.5", 3, "\x5F\xC1\x11", "\x10\x05", 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", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.16.6", 3, "\x5F\xC1\x12", "\x10\x06", 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", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.16.7", 3, "\x5F\xC1\x13", "\x10\x07", 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", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.16.8", 3, "\x5F\xC1\x14", "\x10\x08", 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", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.16.9", 3, "\x5F\xC1\x15", "\x10\x09", 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", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.16.10", 3, "\x5F\xC1\x16", "\x10\x0A", 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", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.16.11", 3, "\x5F\xC1\x17", "\x10\x0B", 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", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.16.12", 3, "\x5F\xC1\x18", "\x10\x0C", 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", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.16.13", 3, "\x5F\xC1\x19", "\x10\x0D", 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", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.16.14", 3, "\x5F\xC1\x1A", "\x10\x0E", 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", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.16.15", 3, "\x5F\xC1\x1B", "\x10\x0F", 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", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.16.16", 3, "\x5F\xC1\x1C", "\x10\x10", 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", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.16.17", 3, "\x5F\xC1\x1D", "\x10\x11", 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", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.16.18", 3, "\x5F\xC1\x1E", "\x10\x12", 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", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.16.19", 3, "\x5F\xC1\x1F", "\x10\x13", 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", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.16.20", 3, "\x5F\xC1\x20", "\x10\x14", PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT | PIV_OBJECT_NEEDS_VCI}, { PIV_OBJ_IRIS_IMAGE, "Cardholder Iris Images", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.16.21", 3, "\x5F\xC1\x21", "\x10\x15", 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, "\x7F\x61", "\x10\x16", 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, "\x5F\xC1\x22", "\x10\x17", 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, "\x5F\xC1\x23", "\x10\x18", 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", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.9999.3", 2, "\x9B\x03", "\x9B\x03", 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", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.9999.20", 2, "\x9A\x06", "\x9A\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_9C06, "Pub 9C key from last genkey", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.9999.21", 2, "\x9C\x06", "\x9C\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_9D06, "Pub 9D key from last genkey", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.9999.22", 2, "\x9D\x06", "\x9D\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_9E06, "Pub 9E key from last genkey", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.9999.23", 2, "\x9E\x06", "\x9E\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_8206, "Pub 82 key ", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.9999.101", 2, "\x82\x06", "\x82\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_8306, "Pub 83 key ", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.9999.102", 2, "\x83\x06", "\x83\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_8406, "Pub 84 key ", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.9999.103", 2, "\x84\x06", "\x84\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_8506, "Pub 85 key ", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.9999.104", 2, "\x85\x06", "\x85\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_8606, "Pub 86 key ", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.9999.105", 2, "\x86\x06", "\x86\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_8706, "Pub 87 key ", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.9999.106", 2, "\x87\x06", "\x87\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_8806, "Pub 88 key ", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.9999.107", 2, "\x88\x06", "\x88\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_8906, "Pub 89 key ", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.9999.108", 2, "\x89\x06", "\x89\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_8A06, "Pub 8A key ", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.9999.109", 2, "\x8A\x06", "\x8A\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_8B06, "Pub 8B key ", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.9999.110", 2, "\x8B\x06", "\x8B\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_8C06, "Pub 8C key ", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.9999.111", 2, "\x8C\x06", "\x8C\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_8D06, "Pub 8D key ", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.9999.112", 2, "\x8D\x06", "\x8D\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_8E06, "Pub 8E key ", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.9999.113", 2, "\x8E\x06", "\x8E\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_8F06, "Pub 8F key ", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.9999.114", 2, "\x8F\x06", "\x8F\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_9006, "Pub 90 key ", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.9999.115", 2, "\x90\x06", "\x90\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_9106, "Pub 91 key ", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.9999.116", 2, "\x91\x06", "\x91\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_9206, "Pub 92 key ", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.9999.117", 2, "\x92\x06", "\x92\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_9306, "Pub 93 key ", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.9999.118", 2, "\x93\x06", "\x93\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_9406, "Pub 94 key ", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.9999.119", 2, "\x94\x06", "\x94\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_9506, "Pub 95 key ", SC_ASN1_APP | 0x13, "2.16.840.1.101.3.7.2.9999.120", 2, "\x95\x06", "\x95\x06", 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 = { "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); #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; for (i = size - 1; c != 0 && i >= 0; i--){ b = c + counter[i]; counter[i] = b & 0xff; c = b>>8; } } /* * Construct SM protected APDU */ static int piv_encode_apdu(sc_card_t *card, sc_apdu_t *plain, sc_apdu_t *sm_apdu) { 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 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 ed_ctx = EVP_CIPHER_CTX_new(); if (ed_ctx == NULL) { r = SC_ERROR_INTERNAL; goto err; } 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; } 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. */ sm_apdu->cse = (plain->cse & ~SC_APDU_SHORT_MASK) | SC_APDU_CASE_4_SHORT; p = header; /* to be included in CMAC */ *p++ = 0x0c; *p++ = plain->ins; *p++ = plain->p1; *p++ = plain->p2; memcpy(p, pad, 12); /* 800-73-4 say padding is 1 to 16 bytes, with 0x80 0x00... */ /* may not need enc_data for cse 1 or 2 */ if (plain->datalen == 0) { enc_datalen = 0; T87len = 0; padlen = 0; } else { enc_datalen = ((plain->datalen + 15) / 16) * 16; /* may add extra 16 bytes */ padlen = enc_datalen - plain->datalen; r = T87len = sc_asn1_put_tag(0x87, NULL, 1 + enc_datalen, NULL, 0, NULL); if (r < 0) goto err; } if (plain->resplen == 0 || plain->le == 0) T97len = 0; sbuflen = T87len + T97len + T8Elen; sbuf = calloc(1, sbuflen); if (sbuf == NULL) { r = SC_ERROR_OUT_OF_MEMORY; goto err; } p = sbuf; if (T87len != 0) { r = sc_asn1_put_tag(0x87, NULL, 1 + enc_datalen, sbuf, sbuflen, &p); if (r != SC_SUCCESS) goto err; *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, 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; } if (T97len) { *p++ = 0x97; *p++ = 0x01; *p++ = plain->le; } macdatalen = p - sbuf; memcpy(priv->sm_session.C_MCV_last, priv->sm_session.C_MCV, MCVlen); /* save is case fails */ #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 *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; 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 */ 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); LOG_FUNC_RETURN(card->ctx, r); } 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); 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); } *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); } LOG_FUNC_RETURN(card->ctx, r); } /* 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; 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 = 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; 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); sc_copy_asn1_entry(c_asn1_sm_response, asn1_sm_response); 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); r = sc_asn1_decode(card->ctx, asn1_sm_response, sm_apdu->resp, sm_apdu->resplen, NULL, NULL); if (r < 0) { sc_log(card->ctx,"SM decode failed"); r = SC_ERROR_SM_AUTHENTICATION_FAILED; goto err; } 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; } 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 (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; } #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 /* MCV is first, then BER TLV Encoded Encrypted PIV Data and Status */ macdatalen = status.value + status.len - sm_apdu->resp; #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 (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 = 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 /* * FIXME TODO - Names and locations of piv_pairing_code and piv_use_sm are likely to change in the future. * See https://github.com/OpenSC/OpenSC/pull/2053/files#r1267388721 */ /* * "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); /* TODO if already verified we could return * may need to verify again, if card reset? */ 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 */ /* TODO check dates and other info as per 800-73-4 */ /* TODO check against off card CA chain if present, * Need opensc.conf options: * where is CA cert chain? * is it required? * check for revocation? * How often to check for revocation? * When is SM used? * Using NFC? * (yes, main point of using SM) * Should reading certificates be done in clear? * (performance vs security) * All crypto operations and PIN ? * (yes for security) */ 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)) /* TODO should be FIPS */ || !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; r = sbuflen = 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 = 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 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); } 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 */ 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); } 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) { free(keydata->exponent); LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); } keydata->pubkey_len = taglen; memcpy (keydata->pubkey, tag, taglen); } } else { /* must be EC */ 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); } } /* TODO: -DEE Could add key to cache so could use engine to generate key, * and sign req in single operation */ r = 0; } err: LOG_FUNC_RETURN(card->ctx, r); } 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_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)); } /* find the PIV AID on the card. If card->type already filled in, * then look for specific AID only */ static int piv_find_aid(sc_card_t * card) { piv_private_data_t * priv = PIV_DATA(card); u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; int r,i; const u8 *tag; size_t taglen; const u8 *nextac; const u8 *pix; size_t pixlen; 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 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 && 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 */ /* There may be more than one 0xAC tag, loop to find all */ nextac = tag; while((actag = sc_asn1_find_tag(card->ctx, nextac, taglen - (nextac - tag), 0xAC, &actaglen)) != NULL) { nextac = actag + actaglen; csai = sc_asn1_find_tag(card->ctx, actag, actaglen, 0x80, &csailen); if (csai != NULL) { if (csailen == 1) { sc_log(card->ctx,"found csID=0x%2.2x",*csai); #ifdef ENABLE_PIV_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; } } #endif /* ENABLE_PIV_SM */ } } } pix = sc_asn1_find_tag(card->ctx, tag, taglen, 0x4F, &pixlen); 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))) { 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); } } } } } LOG_FUNC_RETURN(card->ctx, SC_ERROR_NO_CARD_SUPPORT); } /* * Read a DER encoded object from a file. Allocate and return the buf. * Used to read the file defined in offCardCertURL from a cache. * 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) { int r; int r_tag; int f = -1; size_t len; u8 tagbuf[16]; size_t rbuflen; const u8 * body; unsigned int cla_out, tag_out; size_t bodylen; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); *buf = NULL; *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; } 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; goto err; } body = tagbuf; /* 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_FILE_NOT_FOUND; goto err; } rbuflen = body - tagbuf + bodylen; *buf = malloc(rbuflen); if (!*buf) { r = SC_ERROR_OUT_OF_MEMORY; goto err; } memcpy(*buf, tagbuf, len); /* 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); *buf = NULL; goto err; } } r = (int)rbuflen; *buf_len = rbuflen; err: if (f >= 0) close(f); LOG_FUNC_RETURN(card->ctx, r); } /* the tag is the PIV_OBJ_* */ static int 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, %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; 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); 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) { r = SC_ERROR_OUT_OF_MEMORY; goto err; } } #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_private_data_t * priv = PIV_DATA(card); int r; u8 *rbuf = NULL; size_t rbuflen; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); assert(enumtag >= 0 && enumtag < PIV_OBJ_LAST_ENUM); 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); if (priv->obj_cache[enumtag].obj_len == 0) { r = SC_ERROR_FILE_NOT_FOUND; sc_log(card->ctx, "#%d found but len=0", enumtag); goto err; } *buf = priv->obj_cache[enumtag].obj_data; *buf_len = priv->obj_cache[enumtag].obj_len; r = (int)*buf_len; goto ok; } /* * 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) { sc_log(card->ctx, "no_obj #%d", enumtag); r = SC_ERROR_FILE_NOT_FOUND; goto err; } /* Not cached, try to get it, piv_get_data will allocate a buf */ sc_log(card->ctx, "get #%d", enumtag); rbuflen = 1; r = piv_get_data(card, enumtag, &rbuf, &rbuflen); if (r > 0) { priv->obj_cache[enumtag].flags |= PIV_OBJ_CACHE_VALID; priv->obj_cache[enumtag].obj_len = r; priv->obj_cache[enumtag].obj_data = rbuf; *buf = rbuf; *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 { 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: err: 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; 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, r); } body = sc_asn1_find_tag(card->ctx, priv->obj_cache[enumtag].obj_data, priv->obj_cache[enumtag].obj_len, 0x53, &bodylen); 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) { 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 && 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); if (taglen == 0) LOG_FUNC_RETURN(card->ctx, SC_ERROR_FILE_NOT_FOUND); if(compressed) { priv->obj_cache[enumtag].flags |= PIV_OBJ_CACHE_COMPRESSED; } /* 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; #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 */ } 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); if (taglen == 0) LOG_FUNC_RETURN(card->ctx, SC_ERROR_FILE_NOT_FOUND); 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; } 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); 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_private_data_t * priv = PIV_DATA(card); int enumtag; int r; u8 *rbuf = NULL; size_t rbuflen = 0; const u8 *body; size_t bodylen; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (priv->selected_obj < 0) 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) { /* 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)) { r = SC_ERROR_FILE_NOT_FOUND; goto err; } /* TODO Biometric Information Templates Group Template uses tag 7f61 */ body = sc_asn1_find_tag(card->ctx, rbuf, rbuflen, rbuf[0], &bodylen); if (body == NULL) { /* if missing, assume its the body */ /* DEE bug in the beta card */ sc_log(card->ctx, " ***** tag 0x53 MISSING"); r = SC_ERROR_INVALID_DATA; goto err; } 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); r = SC_ERROR_INVALID_DATA; goto err; } /* if cached obj has internal interesting data (cert or pub key) */ if (priv->return_only_cert || piv_objects[enumtag].flags & PIV_OBJECT_TYPE_PUBKEY) { r = piv_cache_internal_data(card, enumtag); if (r < 0) goto err; } } priv->rwb_state = 0; } 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) count = rbuflen - idx; if (count <= 0) { r = 0; priv->rwb_state = 1; } else { memcpy(buf, rbuf + idx, 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 */ static int piv_put_data(sc_card_t *card, int tag, const u8 *buf, size_t buf_len) { int r; u8 * sbuf; size_t sbuflen; u8 * p; size_t tag_len; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); tag_len = piv_objects[tag].tag_len; 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; 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, 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_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; 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); } 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; 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. 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); out: free(sbuf); LOG_FUNC_RETURN(card->ctx, r); } /* * For certs we need to add the 0x53 tag and other specific tags, * and call the piv_put_data * 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 * 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. * if its not a cert or pubkey its an object. * * Therefore when idx=0, we will get the length of the object * and allocate a buffer, so we can support partial writes. * 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, const u8 *buf, size_t count, unsigned long flags) { piv_private_data_t * priv = PIV_DATA(card); int r; int enumtag; LOG_FUNC_CALLED(card->ctx); if (priv->selected_obj < 0) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); enumtag = piv_objects[priv->selected_obj].enumtag; 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) { 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; 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; } /* on each pass make sure we have w_buf */ if (priv->w_buf == NULL) LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); if (idx + count > priv->w_buf_len) LOG_FUNC_RETURN(card->ctx, SC_ERROR_OBJECT_NOT_VALID); memcpy(priv->w_buf + idx, buf, count); /* copy one chunk */ /* if this was not the last chunk, return to get rest */ if (idx + count < priv->w_buf_len) LOG_FUNC_RETURN(card->ctx, (int)count); 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 = (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) { priv->obj_cache[enumtag].flags |= PIV_OBJ_CACHE_VALID; priv->obj_cache[enumtag].obj_data = priv->w_buf; priv->obj_cache[enumtag].obj_len = priv->w_buf_len; } else { if (priv->w_buf) free(priv->w_buf); } priv->w_buf = NULL; priv->w_buf_len = 0; LOG_FUNC_RETURN(card->ctx, (r < 0)? r : (int)count); } /* * Card initialization is NOT standard. * 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 EVP_CIPHER *get_cipher_for_algo(sc_card_t *card, int alg_id) { const char *algo; switch (alg_id) { 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) { 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; break; case 0x08: *size = 128/8; break; case 0x0A: *size = 192/8; break; case 0x0C: *size = 256/8; break; default: return SC_ERROR_INVALID_ARGUMENTS; } return SC_SUCCESS; } 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; size_t expected_keylen; size_t keylen, readlen; u8 * keybuf = NULL; u8 * tkey = NULL; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); keyfilename = (char *)getenv("PIV_EXT_AUTH_KEY"); if (keyfilename == NULL) { sc_log(card->ctx, "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) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Invalid cipher selector, none found for: %02x", alg_id); r = SC_ERROR_INVALID_ARGUMENTS; goto err; } f = fopen(keyfilename, "rb"); if (!f) { sc_log(card->ctx, " Unable to load key from file\n"); r = SC_ERROR_FILE_NOT_FOUND; goto err; } if (0 > fseek(f, 0L, SEEK_END)) r = SC_ERROR_INTERNAL; fsize = ftell(f); if (0 > (long) fsize) r = SC_ERROR_INTERNAL; if (0 > fseek(f, 0L, SEEK_SET)) r = SC_ERROR_INTERNAL; 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 */ if (!keybuf) { sc_log(card->ctx, " Unable to allocate key memory"); r = SC_ERROR_OUT_OF_MEMORY; goto err; } keybuf[fsize] = 0x00; /* in case it is text need null */ 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; } if (fsize == expected_keylen) { /* it must be binary */ memcpy(tkey, keybuf, expected_keylen); } else { /* if the key-length is larger then binary length, we assume hex encoded */ sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Treating key as hex-encoded!\n"); sc_right_trim(keybuf, fsize); keylen = expected_keylen; r = sc_hex_to_bin((char *)keybuf, tkey, &keylen); 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; goto err; } } *key = tkey; tkey = NULL; *len = expected_keylen; r = SC_SUCCESS; err: if (f) fclose(f); if (keybuf) { free(keybuf); } if (tkey) { free(tkey); } LOG_FUNC_RETURN(card->ctx, r); return r; } #endif /* * will only deal with 3des for now * assumptions include: * size of encrypted data is same as unencrypted * 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) { int r; #ifdef ENABLE_OPENSSL int N; int locked = 0; u8 rbuf[4096]; u8 *nonce = NULL; size_t nonce_len; u8 *p; u8 *key = NULL; size_t keylen; u8 *plain_text = NULL; size_t plain_text_len = 0; u8 *tmp; size_t tmplen, tmplen2; u8 *built = NULL; size_t built_len; const u8 *body = NULL; size_t body_len; const u8 *witness_data = NULL; size_t witness_len; const u8 *challenge_response = NULL; size_t challenge_response_len; u8 *decrypted_reponse = NULL; size_t decrypted_reponse_len; EVP_CIPHER_CTX * ctx = NULL; u8 sbuf[255]; 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(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; } 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"); goto err; } r = sc_lock(card); if (r != SC_SUCCESS) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "sc_lock failed\n"); goto err; /* cleanup */ } locked = 1; p = sbuf; *p++ = 0x7C; *p++ = 0x02; *p++ = 0x80; *p++ = 0x00; /* get the encrypted nonce */ r = piv_general_io(card, 0x87, alg_id, key_ref, sbuf, p - sbuf, 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 || rbuf[0] != 0x7C) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Invalid Witness Data response of NULL\n"); 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 == 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; goto err; } /* Allocate an output buffer for openssl */ plain_text = malloc(witness_len); if (!plain_text) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not allocate buffer for plain text\n"); r = SC_ERROR_INTERNAL; goto err; } /* 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); p = plain_text; 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)) { sc_log_openssl(card->ctx); r = SC_ERROR_INTERNAL; goto err; } tmplen = N; plain_text_len += tmplen; 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); r = SC_ERROR_INTERNAL; goto err; } /* Build a response to the card of: * [GEN AUTH][ 8081 ] * Start by computing the nonce for the * nonce length should match the witness length of * the card. */ nonce = malloc(witness_len); 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); r = SC_ERROR_INTERNAL; goto err; } nonce_len = witness_len; 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); r = SC_ERROR_INTERNAL; goto err; } /* nonce for challenge */ 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 */ 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 */ r = sc_asn1_put_tag(0x7C, NULL, tmplen, NULL, 0, NULL); if (r <= 0) { r = SC_ERROR_INTERNAL; goto err; } built_len = r; /* Build the response buffer */ p = built = malloc(built_len); if(!built) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "OOM Building witness response and challenge\n"); r = SC_ERROR_INTERNAL; goto err; } p = built; /* Start with the 7C Tag */ r = sc_asn1_put_tag(0x7C, NULL, tmplen2, p, built_len, &p); if (r != SC_SUCCESS) { goto err; } /* Add the DECRYPTED witness, tag 0x80 */ 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 */ 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, 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 || rbuf[0] != 0x7C) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not find outer tag 0x7C in response"); 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) { challenge_response = sc_asn1_find_tag(card->ctx, body, body_len, 0x80, &challenge_response_len); 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; goto err; } } /* Decrypt challenge and check against nonce */ decrypted_reponse = malloc(challenge_response_len); if(!decrypted_reponse) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "OOM Allocating decryption buffer"); r = SC_ERROR_INVALID_DATA; goto err; } 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); tmp = decrypted_reponse; 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)) { sc_log_openssl(card->ctx); r = SC_ERROR_INTERNAL; goto err; } tmplen = N; decrypted_reponse_len += tmplen; 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); 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 (decrypted_reponse) free(decrypted_reponse); if (built) free(built); if (plain_text) free(plain_text); if (nonce) free(nonce); if (key) free(key); #else sc_log(card->ctx, "OpenSSL Required"); r = SC_ERROR_NOT_SUPPORTED; #endif /* ENABLE_OPENSSL */ LOG_FUNC_RETURN(card->ctx, r); } /* Currently only used for card administration */ static int piv_general_external_authenticate(sc_card_t *card, unsigned int key_ref, unsigned int alg_id) { int r; #ifdef ENABLE_OPENSSL size_t tmplen; int outlen; int locked = 0; u8 *p; u8 rbuf[4096]; u8 *key = NULL; u8 *cipher_text = NULL; u8 *output_buf = NULL; const u8 *body = NULL; const u8 *challenge_data = NULL; size_t body_len; size_t output_len; size_t challenge_len; size_t keylen = 0; size_t cipher_text_len = 0; u8 sbuf[255]; 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) { 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(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; } 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"); goto err; } r = sc_lock(card); if (r != SC_SUCCESS) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "sc_lock failed\n"); goto err; /* cleanup */ } locked = 1; p = sbuf; *p++ = 0x7C; *p++ = 0x02; *p++ = 0x81; *p++ = 0x00; /* get a challenge */ 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; } /* * the value here corresponds with the response size, so we use this * to alloc the response buffer, rather than re-computing it. */ output_len = r; /* 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 || rbuf[0] != 0x7C) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Invalid Challenge Data response of NULL\n"); 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); if (!challenge_data) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Invalid Challenge Data none found in TLV\n"); r = SC_ERROR_INVALID_DATA; goto err; } /* 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; } 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, 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; } cipher_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; } cipher_text_len += outlen; /* * Actually perform the sanity check on lengths plaintext length vs * encrypted length */ 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) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not allocate output buffer: %s\n", strerror(errno)); r = SC_ERROR_INTERNAL; goto err; } p = output_buf; /* * Build: 7C[82] * Start off by capturing the data of the response: * - 82 * Build the outside TLV (7C) * Advance past that tag + len * Build the body (82) * memcopy the body past the 7C portion * Transmit */ tmplen = sc_asn1_put_tag(0x82, NULL, cipher_text_len, NULL, 0, NULL); if (tmplen <= 0) { r = SC_ERROR_INTERNAL; goto err; } 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 */ 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 */ 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: %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, 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); if (locked) sc_unlock(card); if (key) { sc_mem_clear(key, keylen); free(key); } if (cipher_text) free(cipher_text); if (output_buf) free(output_buf); #else sc_log(card->ctx, "OpenSSL Required"); r = SC_ERROR_NOT_SUPPORTED; #endif /* ENABLE_OPENSSL */ 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) { int r; int i; u8 gbits; u8 *rbuf = NULL; const u8 *body; const u8 *fascn; const u8 *guid; size_t rbuflen = 0, bodylen, fascnlen, guidlen; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (card->serialnr.len) { *serial = card->serialnr; LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } /* * 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. */ r = piv_get_cached_data(card, PIV_OBJ_CHUI, &rbuf, &rbuflen); LOG_TEST_RET(card->ctx, r, "Failure retrieving CHUI"); 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 && rbuf[0] == 0x53) { fascn = sc_asn1_find_tag(card->ctx, body, bodylen, 0x30, &fascnlen); /* Find the FASC-N data */ 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 + parity code) */ 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) { /* guidlen is 16 */ serial->len = guidlen; memcpy (serial->value, guid, serial->len); r = SC_SUCCESS; } } } card->serialnr = *serial; LOG_FUNC_RETURN(card->ctx, r); } /* * If the object can not be present on the card, because the History * object is not present or the History object says its not present, * return 1. If object may be present return 0. * Cuts down on overhead, by not showing non existent objects to pkcs11 * The path for the object is passed in and the first 2 bytes are used. * Note: If the History or Discovery object is not found the * PIV_OBJ_CACHE_NOT_PRESENT is set, as older cards do not have these. * pkcs15-piv.c calls this via cardctl. */ static int piv_is_object_present(sc_card_t *card, u8 *ptr) { piv_private_data_t * priv = PIV_DATA(card); int r = 0; int enumtag; enumtag = piv_find_obj_by_containerid(card, ptr); if (enumtag >= 0 && priv->obj_cache[enumtag].flags & PIV_OBJ_CACHE_NOT_PRESENT) r = 1; LOG_FUNC_RETURN(card->ctx, r); } /* * NIST 800-73-3 allows the default pin to be the PIV application 0x80 * 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 *pin_ref) { piv_private_data_t * priv = PIV_DATA(card); *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) { piv_private_data_t * priv = PIV_DATA(card); u8 * opts; /* A or M, key_ref, alg_id */ LOG_FUNC_CALLED(card->ctx); 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; } 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) { /* 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); LOG_FUNC_CALLED(card->ctx); if (priv->card_issues & CI_NO_RANDOM) { r = SC_ERROR_NOT_SUPPORTED; LOG_TEST_GOTO_ERR(card->ctx, r, "No support for random data"); } /* 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 = 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"); } 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"); } if (len < out_len) { out_len = len; } memcpy(rnd, p, out_len); 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); int r = 0; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_log(card->ctx, "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; if (env->algorithm == SC_ALGORITHM_RSA) { priv->alg_id = 0x06; /* Say it is RSA, set 5, 6, 7 later */ } 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; } } else r = SC_ERROR_NO_CARD_SUPPORT; } else 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) { 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) { piv_private_data_t * priv = PIV_DATA(card); int r, tmplen, tmplen2; u8 *p; const unsigned char *p2; size_t taglen; size_t bodylen; unsigned int cla, tag; unsigned int real_alg_id, op_tag; u8 sbuf[4096]; /* needs work. for 3072 keys, needs 384+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; 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) { 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 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); } } /* EC alg_id was already set */ r = piv_general_io(card, 0x87, real_alg_id, priv->key_ref, sbuf, p - sbuf, rbuf, sizeof rbuf); if (r < 0) goto err; 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"); } 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 (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_private_data_t * priv = PIV_DATA(card); int r; size_t nLen; u8 rbuf[128]; /* For EC conversions 384 will fit */ SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); /* The PIV returns a DER SEQUENCE{INTEGER, INTEGER} * 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 * 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) { 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); r = SC_ERROR_INVALID_DATA; goto err; } r = piv_validate_general_authentication(card, data, datalen, rbuf, sizeof rbuf); if (r < 0) goto err; r = sc_asn1_decode_ecdsa_signature(card->ctx, rbuf, r, nLen, &out, outlen); } else { /* RSA is all set */ r = piv_validate_general_authentication(card, data, datalen, out, outlen); } err: 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) { SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, piv_validate_general_authentication(card, data, datalen, out, outlen)); } /* * the PIV-II does not always support files, but we will simulate * files and reading/writing using get/put_data * The path is the containerID number * We can use this to determine the type of data requested, like a cert * or pub key. * We only support write from the piv_tool with file_out==NULL * 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) { piv_private_data_t * priv = PIV_DATA(card); int r; int i; const u8 *path; size_t pathlen; sc_file_t *file = NULL; u8 * rbuf = NULL; size_t rbuflen = 0; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); path = in_path->value; pathlen = in_path->len; /* only support single EF in current application */ /* * 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 * thus opensc-tool and opensc-explorer can not read the emulated files */ if (memcmp(path, "\x3F\x00", 2) == 0) { if (pathlen > 2) { path += 2; pathlen -= 2; } } i = piv_find_obj_by_containerid(card, path); if (i < 0) LOG_FUNC_RETURN(card->ctx, SC_ERROR_FILE_NOT_FOUND); /* * pkcs15 will use a 2 byte path or a 4 byte path * with cece added to path to request only the cert from the cert obj * PIV "Container ID" is used as the path, and are two bytes long */ priv->return_only_cert = (pathlen == 4 && path[2] == 0xce && path[3] == 0xce); priv->selected_obj = i; priv->rwb_state = -1; /* make it look like the file was found. */ /* We don't want to read it now unless we need the length */ if (file_out) { /* we need to read it now, to get length into cache */ r = piv_get_cached_data(card, i, &rbuf, &rbuflen); if (r < 0) LOG_FUNC_RETURN(card->ctx, SC_ERROR_FILE_NOT_FOUND); /* get the cert or the pub key out and into the cache too */ if (priv->return_only_cert || piv_objects[i].flags & PIV_OBJECT_TYPE_PUBKEY) { r = piv_cache_internal_data(card, i); if (r < 0) LOG_FUNC_RETURN(card->ctx, r); } file = sc_file_new(); if (file == NULL) LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); file->path = *in_path; /* this could be like the FCI */ file->type = SC_FILE_TYPE_DF; file->shareable = 0; file->ef_structure = 0; if (priv->return_only_cert) file->size = priv->obj_cache[i].internal_obj_len; else file->size = priv->obj_cache[i].obj_len; 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_parse_discovery(sc_card_t *card, u8 * rbuf, size_t rbuflen, int aid_only) { piv_private_data_t * priv = PIV_DATA(card); int r = 0; const u8 * body; size_t bodylen; const u8 * aid; size_t aidlen; const u8 * pinp; size_t pinplen; unsigned int cla_out, tag_out; if (rbuflen != 0) { body = rbuf; 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); aidlen = 0; aid = sc_asn1_find_tag(card->ctx, body, bodylen, 0x4F, &aidlen); 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); if (pinp && pinplen == 2) { 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. */ static int piv_process_history(sc_card_t *card) { piv_private_data_t * priv = PIV_DATA(card); int r; int i, tmplen, tmplen2, tmplen3; int enumtag; u8 * rbuf = NULL; size_t rbuflen = 0; const u8 * body; size_t bodylen; const u8 * num; size_t numlen; const u8 * url = NULL; size_t urllen; u8 * ocfhfbuf = NULL; unsigned int cla_out, tag_out; size_t ocfhflen; const u8 * seq; const u8 * seqtag; size_t seqlen; const u8 * keyref; size_t keyreflen; const u8 * cert; size_t certlen; size_t certobjlen, i2; 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 */ 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 */ } /* 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 || ((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 (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_INVALID_ASN1_OBJECT; goto err; } priv->keysWithOnCardCerts = *num; } 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_INVALID_ASN1_OBJECT; goto err; } priv->keysWithOffCardCerts = *num; } url = sc_asn1_find_tag(card->ctx, body, bodylen, 0xF3, &urllen); if (url) { 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 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"); /* 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; /* * If user has gotten copy of the file from the offCardCertsURL, * we will read in and add the certs to the cache as listed on * 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 * in the directory used for the PKCS15 cache */ r = 0; if (priv->offCardCertURL) { char * fp; char filename[PATH_MAX]; if (strncmp("http://", priv->offCardCertURL, 7)) { r = SC_ERROR_INVALID_DATA; goto err; } /* find the last / so we have the filename part */ fp = strrchr(priv->offCardCertURL + 7,'/'); if (fp == NULL) { r = SC_ERROR_INVALID_DATA; goto err; } fp++; /* 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,"\\"); #else strcat(filename,"/"); #endif strcat(filename,fp); r = piv_read_obj_from_file(card, filename, &ocfhfbuf, &ocfhflen); if (r == SC_ERROR_FILE_NOT_FOUND) { r = 0; goto err; } /* * Its a seq of seq of a key ref and cert */ body = ocfhfbuf; 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; } seq = body; while (bodylen > 0) { seqtag = seq; 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)) { sc_log(card->ctx, "DER problem"); r = SC_ERROR_INVALID_ASN1_OBJECT; goto err; } cert = keyref + keyreflen; certlen = seqlen - (cert - seq); enumtag = PIV_OBJ_RETIRED_X509_1 + *keyref - 0x82; /* now add the cert like another object */ 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; 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; 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; priv->obj_cache[enumtag].flags |= PIV_OBJ_CACHE_VALID; 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); 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); bodylen -= (seqlen + seq - seqtag); seq += seqlen; } } err: if (ocfhfbuf) free(ocfhfbuf); 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); int i; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (priv) { if (priv->context_specific) { sc_log(card->ctx, "Clearing CONTEXT_SPECIFIC lock"); priv->context_specific = 0; sc_unlock(card); } free(priv->aid_der.value); if (priv->w_buf) free(priv->w_buf); if (priv->offCardCertURL) free(priv->offCardCertURL); for (i = 0; i < PIV_OBJ_LAST_ENUM - 1; i++) { 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: break; default: return 0; /* can not handle the card */ } 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); if (r < 0 || !card->drv_data) { /* clean up what we left in card */ piv_finish(card); return 0; /* match failed */ } sc_debug(card->ctx,SC_LOG_DEBUG_MATCH, "PIV_MATCH card->type:%d r:%d\n", card->type,r); return 1; /* matched */ } 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 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: 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 * 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. */ if (card->reader->atr_info.hist_bytes != NULL) { 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 && !(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; } /* 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; 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 == 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; } } } } } 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 */ 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 */ } } card->type = type; /* 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); 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 */ /* TODO Dual CAC/PIV are bases on 800-73-1 where priv->pin_preference = 0. need to check later */ priv->logged_in = SC_PIN_STATE_UNKNOWN; 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 */ 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; } 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 use the AID */ if (card->type != SC_CARD_TYPE_PIV_II_BASE) r = piv_find_discovery(card); else r = SC_CARD_TYPE_UNKNOWN; if (r < 0) { piv_obj_cache_free_entry(card, PIV_OBJ_DISCOVERY, 0); /* don't cache on failure */ r = piv_find_aid(card); } /*if both fail, its not a PIV card */ if (r < 0) { goto err; } /* Assumes all Yubikey cards are identified via ATR Historic bytes */ 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 = yubico_version_buf; apdu.resplen = sizeof(yubico_version_buf); apdu.le = apdu.resplen; r2 = sc_transmit_apdu(card, &apdu); /* on error yubico_version == 0 */ if (r2 >= 3) { priv->yubico_version = (yubico_version_buf[0]<<16) | (yubico_version_buf[1] <<8) | yubico_version_buf[2]; sc_log(card->ctx, "Yubico card->type=%d, r=0x%08x version=0x%08x", card->type, r, priv->yubico_version); } } sc_debug(card->ctx,SC_LOG_DEBUG_MATCH, "PIV_MATCH card->type:%d r2:%d CI:%08x r:%d\n", card->type, r2, priv->card_issues, r); /* 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) { /* * 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 ccc_flags:%08x CI:%08x r:%d\n", card->type, r2, priv->ccc_flags, priv->card_issues, r); /* 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; 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\n", card->type, r2, priv->card_issues, r); /* 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_800_73_4: r2 = piv_find_aid(card); } } /* If SM is supported, set SC_CARD_TYPE_PIV_II_800_73_4 */ if (priv->init_flags & PIV_INIT_AID_AC) { card->type = SC_CARD_TYPE_PIV_II_800_73_4; } sc_debug(card->ctx,SC_LOG_DEBUG_MATCH, "PIV_MATCH card->type:%d r2:%d CI:%08x r:%d\n", card->type, r2, priv->card_issues, r); #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 sc_debug(card->ctx,SC_LOG_DEBUG_MATCH, "PIV_MATCH card->type:%d r2:%d CI:%08x r:%d\n", card->type, r2, priv->card_issues, r); /* * 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. */ sc_debug(card->ctx,SC_LOG_DEBUG_MATCH, "PIV_MATCH card->type:%d CI:%08x r:%d\n", card->type, priv->card_issues, r); 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_OTHER_AID_LOSE_STATE | CI_LEAKS_FILE_NOT_FOUND; if (priv->yubico_version < 0x00040302) priv->card_issues |= CI_VERIFY_LC0_FAIL; 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; 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; /* TODO may need more research */ 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_EC384 | CI_NO_EC | CI_NO_RANDOM; /* does not have 9B key */ /* Discovery object returns 6A 82 so is not on card by default */ /* TODO may need more research */ 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 card-type=%d card_issues=0x%08x", card->type, priv->card_issues); sc_debug(card->ctx,SC_LOG_DEBUG_MATCH, "PIV_MATCH card->type:%d r2:%d CI:%08x r:%d\n", card->type, r2, priv->card_issues, r); 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_debug(card->ctx,SC_LOG_DEBUG_MATCH, "PIV_MATCH card->type:%d r2:%d CI:%08x r:%d\n", card->type, r2, priv->card_issues, r); /* 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 card->type:%d r2:%d CI:%08x r:%d\n", card->type, r2, priv->card_issues, r); /* 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 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); } } /* 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", card->max_send_size, card->max_recv_size, card->type); card->cla = 0x00; if (card->name == NULL) card->name = card->driver->name; priv->enumtag = piv_aids[0].enumtag; /* PKCS#11 may try to generate session keys, and get confused * if SC_ALGORITHM_ONBOARD_KEY_GEN is present * piv-tool can still do this, just don't tell PKCS#11 */ flags = SC_ALGORITHM_RSA_RAW; if (card->type == SC_CARD_TYPE_PIV_II_SWISSBIT) { flags |= SC_ALGORITHM_ONBOARD_KEY_GEN; } _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 */ 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; for (i = 0; ec_curves[i].oid.value[0] >= 0; i++) { 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); } } if (!(priv->card_issues & CI_NO_RANDOM)) card->caps |= SC_CARD_CAP_RNG; /* 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-4 * Response of AID says if SM is supported. Look for Cipher Suite */ if (priv->csID && priv->cs != NULL) { /* * TODO look closer at reset of card by other process * Main point in SM and VCI is to allow contactless access */ /* Only piv_init and piv_reader_lock_obtained should call piv_sm_open */ /* 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 */ } 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) { struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); int r; #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 */ 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! */ 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) { sc_log(card->ctx, "Verification failed (remaining tries: %d)", (sw2 & 0x0f)); return SC_ERROR_PIN_CODE_INCORRECT; /* this is what the iso_check_sw returns for 63 C0 */ } } } } #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); u8 buf[8]; /* tag of 53 with 82 xx xx will fit in 4 */ u8 * rbuf; size_t buf_len; static int protected_objects[] = {PIV_OBJ_PI, PIV_OBJ_CHF, PIV_OBJ_IRIS_IMAGE}; LOG_FUNC_CALLED(card->ctx); /* * routine only called from piv_pin_cmd after verify lc=0 did not return 90 00 * We will test for a protected object using GET DATA. * * Based on observations, of cards using the GET DATA APDU, * SC_ERROR_SECURITY_STATUS_NOT_SATISFIED means the PIN not verified, * SC_SUCCESS means PIN has been verified even if it has length 0 * SC_ERROR_FILE_NOT_FOUND (which is the bug) does not tell us anything * about the state of the PIN and we will try the next object. * * 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 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++) { buf_len = sizeof(buf); rbuf = buf; r = piv_get_data(card, protected_objects[i], &rbuf, &buf_len); /* 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 */ priv->object_test_verify = protected_objects[i]; break; } } if (priv->object_test_verify == 0) { /* * none of the objects returned acceptable sw1, sw2 */ sc_log(card->ctx, "No protected objects found, setting CI_CANT_USE_GETDATA_FOR_STATE"); priv->card_issues |= CI_CANT_USE_GETDATA_FOR_STATE; r = SC_ERROR_PIN_CODE_INCORRECT; } } else { /* use the one object we found earlier. Test is security status has changed */ buf_len = sizeof(buf); rbuf = buf; r = piv_get_data(card, priv->object_test_verify, &rbuf, &buf_len); } if (r == SC_ERROR_FILE_NOT_FOUND) r = SC_ERROR_PIN_CODE_INCORRECT; else if (r == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED) r = SC_ERROR_PIN_CODE_INCORRECT; 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); LOG_FUNC_RETURN(card->ctx, r); } static int piv_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left) { int r = 0; 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 * FIPS 140-2 (6 character minimum) requirements. */ struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); 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) { size_t i = 0; if (data->pin2.len < 6) { return SC_ERROR_INVALID_PIN_LENGTH; } for(i=0; i < data->pin2.len; ++i) { if (!isdigit(data->pin2.data[i])) { return SC_ERROR_INVALID_DATA; } } } priv->pin_cmd_verify_sw1 = 0x00U; 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 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 = 0; 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 * a “PIN Always” or “OCC Always” access rule. In other words, the * PIN or OCC data must be submitted and verified every time * immediately before a digital signature key operation. This * ensures cardholder participation every time the private key is * used for digital signature generation */ LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } } /* * 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); 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"); /* TODO maybe true for other contactless cards */ 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) { sc_log(card->ctx, "AID may be lost doing piv_find_aid and retry pin_cmd"); 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); priv->pin_cmd_verify = 0; } /* 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; } /* Some cards never return 90 00 for SC_PIN_CMD_GET_INFO even if the card state is verified */ /* PR 797 has changed the return codes from pin_cmd, and added a data->pin1.logged_in flag */ 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); if (r == SC_SUCCESS) data->pin1.logged_in = SC_PIN_STATE_LOGGED_IN; 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; } r = SC_SUCCESS; } } priv->logged_in = data->pin1.logged_in; 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); LOG_FUNC_RETURN(card->ctx, r); } static int piv_logout(sc_card_t *card) { int r = SC_ERROR_INTERNAL; piv_private_data_t * priv = PIV_DATA(card); LOG_FUNC_CALLED(card->ctx); 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, */ /* TODO 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. */ /* TODO may be called recursively to handle reset. * need we are active, and if called again with was_reset save this * and return to let first call handle the reset */ static int piv_card_reader_lock_obtained(sc_card_t *card, int was_reset) { int r = 0; u8 temp[SC_MAX_APDU_BUFFER_SIZE]; size_t templen = sizeof(temp); piv_private_data_t * priv = PIV_DATA(card); /* may be null */ SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); /* 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; } 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 * TODO may be with reset? */ 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 = piv_select_aid(card, piv_aids[0].value, piv_aids[0].len_short, temp, &templen); sc_debug(card->ctx,SC_LOG_DEBUG_MATCH, "piv_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) { struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); piv_ops = *iso_drv->ops; piv_ops.match_card = piv_match_card; 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.get_challenge = piv_get_challenge; piv_ops.logout = piv_logout; piv_ops.read_binary = piv_read_binary; piv_ops.write_binary = piv_write_binary; 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.check_sw = piv_check_sw; piv_ops.card_ctl = piv_card_ctl; piv_ops.pin_cmd = piv_pin_cmd; piv_ops.card_reader_lock_obtained = piv_card_reader_lock_obtained; return &piv_drv; } struct sc_card_driver * sc_get_piv_driver(void) { return sc_get_driver(); } OpenSC-0.26.1/src/libopensc/card-rtecp.c000066400000000000000000000627301474147347300177120ustar00rootroot00000000000000/* * card-rtecp.c: Support for Rutoken ECP and Rutoken Lite cards * * Copyright (C) 2009 Aleksey Samsonov * * 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 "asn1.h" #include "cardctl.h" 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 and Lite driver", "rutoken_ecp", &rtecp_ops, NULL, 0, NULL }; 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_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_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 } }; static int rtecp_match_card(sc_card_t *card) { int i = -1; i = _sc_match_atr(card, rtecp_atrs, &card->type); if (i >= 0) { card->name = rtecp_atrs[i].name; LOG_FUNC_RETURN(card->ctx, 1); } LOG_FUNC_RETURN(card->ctx, 0); } static int rtecp_init(sc_card_t *card) { sc_algorithm_info_t info; unsigned long flags; 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; _sc_card_add_rsa_alg(card, 256, 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); _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, 4096, flags, 0); memset(&info, 0, sizeof(info)); info.algorithm = SC_ALGORITHM_GOSTR3410; info.key_length = 256; info.flags = SC_ALGORITHM_GOSTR3410_RAW | SC_ALGORITHM_ONBOARD_KEY_GEN | SC_ALGORITHM_GOSTR3410_HASH_NONE; _sc_card_add_algorithm(card, &info); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); } static void reverse(unsigned char *buf, size_t len) { unsigned char tmp; size_t i; if (!buf && len != 0) return; for (i = 0; i < len / 2; ++i) { tmp = buf[i]; buf[i] = buf[len - 1 - i]; buf[len - 1 - i] = tmp; } } static unsigned int sec_attr_to_method(unsigned int attr) { if (attr == 0xFF) return SC_AC_NEVER; else if (attr == 0) return SC_AC_NONE; else if (attr & 0x03) return SC_AC_CHV; else return SC_AC_UNKNOWN; } static unsigned long sec_attr_to_key_ref(unsigned int attr) { if (attr == 1 || attr == 2) return attr; return 0; } static unsigned int to_sec_attr(unsigned int method, unsigned int key_ref) { if (method == SC_AC_NEVER || method == SC_AC_NONE) return method; if (method == SC_AC_CHV && (key_ref == 1 || key_ref == 2)) return key_ref; return 0; } static void set_acl_from_sec_attr(sc_card_t *card, sc_file_t *file) { unsigned int method; unsigned long key_ref; 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_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); } if (file->sec_attr[0] & 0x01) /* if AccessMode.0 */ { method = sec_attr_to_method(file->sec_attr[1 + 0]); key_ref = sec_attr_to_key_ref(file->sec_attr[1 + 0]); sc_log(card->ctx, (file->type == SC_FILE_TYPE_DF) ? "SC_AC_OP_CREATE %i %lu\n" : "SC_AC_OP_READ %i %lu\n", (int)method, key_ref); sc_file_add_acl_entry(file, (file->type == SC_FILE_TYPE_DF) ? SC_AC_OP_CREATE : SC_AC_OP_READ, method, key_ref); } if (file->type == SC_FILE_TYPE_DF) { sc_file_add_acl_entry(file, SC_AC_OP_LIST_FILES, SC_AC_NONE, SC_AC_KEY_REF_NONE); } else if (file->sec_attr[0] & 0x02) /* if AccessMode.1 */ { method = sec_attr_to_method(file->sec_attr[1 + 1]); key_ref = sec_attr_to_key_ref(file->sec_attr[1 + 1]); 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_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); } } static int set_sec_attr_from_acl(sc_card_t *card, sc_file_t *file) { const sc_acl_entry_t *entry; u8 sec_attr[SC_RTECP_SEC_ATTR_SIZE] = { 0 }; int r; 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) { sec_attr[0] |= 0x40; sec_attr[1 + 6] = to_sec_attr(entry->method, entry->key_ref); } if (file->type == SC_FILE_TYPE_DF) { entry = sc_file_get_acl_entry(file, SC_AC_OP_CREATE); if (entry) { /* ATTR: Create DF/EF file */ sec_attr[0] |= 0x01; sec_attr[1 + 0] = to_sec_attr(entry->method, entry->key_ref); /* ATTR: Create Internal EF (RSF) file */ sec_attr[0] |= 0x02; sec_attr[1 + 1] = to_sec_attr(entry->method, entry->key_ref); } } else { entry = sc_file_get_acl_entry(file, SC_AC_OP_READ); if (entry) { sec_attr[0] |= 0x01; sec_attr[1 + 0] = to_sec_attr(entry->method, entry->key_ref); } entry = sc_file_get_acl_entry(file, SC_AC_OP_WRITE); if (entry) { sec_attr[0] |= 0x02; sec_attr[1 + 1] = to_sec_attr(entry->method, entry->key_ref); } entry = sc_file_get_acl_entry(file, SC_AC_OP_UPDATE); if (entry) { /* rewrite if sec_attr[1 + 1] already set */ sec_attr[0] |= 0x02; sec_attr[1 + 1] = to_sec_attr(entry->method, entry->key_ref); } } /* FIXME: Find the best solution */ if (file->path.len == 2 && !memcmp(file->path.value, "\x3F\x00", 2)) { /* ATTR: Put data */ sec_attr[0] |= 0x04; sec_attr[1 + 2] = 1; /* so-pin reference */ } r = sc_file_set_sec_attr(file, sec_attr, sizeof(sec_attr)); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } static int rtecp_select_file(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file_out) { 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); 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_VERBOSE, SC_ERROR_NOT_SUPPORTED); } // 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); if (file->sec_attr && file->sec_attr_len == SC_RTECP_SEC_ATTR_SIZE) set_acl_from_sec_attr(card, file); else { 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); } static int rtecp_verify(sc_card_t *card, unsigned int type, int ref_qualifier, const u8 *data, size_t data_len, int *tries_left) { sc_apdu_t apdu; int r, send_logout = 0; (void)type; /* no warning */ if (!card || !card->ctx || !data) return SC_ERROR_INVALID_ARGUMENTS; for (;;) { sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x20, 0, ref_qualifier); apdu.lc = data_len; apdu.data = data; apdu.datalen = data_len; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (send_logout++ == 0 && apdu.sw1 == 0x6F && apdu.sw2 == 0x86) { r = sc_logout(card); LOG_TEST_RET(card->ctx, r, "Logout failed"); } else break; } if (apdu.sw1 == 0x63 && apdu.sw2 == 0) { /* Verification failed */ sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x20, 0, ref_qualifier); 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 == SC_ERROR_PIN_CODE_INCORRECT && tries_left) *tries_left = (int)(apdu.sw2 & 0x0F); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } static int rtecp_logout(sc_card_t *card) { sc_apdu_t apdu; int r; 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); 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) { sc_apdu_t apdu; u8 *buf, *buf_out; size_t i; int r; 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); LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); } for (i = 0; i < data_len; ++i) buf[i] = data[data_len - 1 - i]; if (sign) sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x9E, 0x9A); else sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x80, 0x86); apdu.lc = data_len; apdu.data = buf; apdu.datalen = data_len; apdu.resp = buf_out; apdu.resplen = out_len + 2; apdu.le = out_len > 256 ? 256 : out_len; if (apdu.lc > 255) apdu.flags |= SC_APDU_FLAGS_CHAINING; r = sc_transmit_apdu(card, &apdu); if (!sign) sc_mem_clear(buf, data_len); free(buf); if (r) sc_log(card->ctx, "APDU transmit failed: %s\n", sc_strerror(r)); else { if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { for (i = 0; i < apdu.resplen; ++i) out[i] = buf_out[apdu.resplen - 1 - i]; r = (i > 0) ? (int)i : SC_ERROR_INTERNAL; } else r = sc_check_sw(card, apdu.sw1, apdu.sw2); } if (!sign) sc_mem_clear(buf_out, out_len + 2); free(buf_out); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } static int rtecp_decipher(sc_card_t *card, const u8 *data, size_t data_len, u8 *out, size_t out_len) { int r; 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); } static int rtecp_compute_signature(sc_card_t *card, const u8 *data, size_t data_len, u8 *out, size_t out_len) { int r; 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); } static int rtecp_change_reference_data(sc_card_t *card, unsigned int type, int ref_qualifier, const u8 *old, size_t oldlen, 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, transmits_num; int r; if (!card || !card->ctx || !newref) return SC_ERROR_INVALID_ARGUMENTS; sc_log(card->ctx, "newlen = %"SC_FORMAT_LEN_SIZE_T"u\n", newlen); if (newlen > 0xFFFF) 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); LOG_TEST_RET(card->ctx, r, "Verify old pin failed"); } max_transmit_length = sc_get_max_send_size(card); 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); p = buf = (u8 *)malloc(buf_length); if (buf == NULL) 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); /* put 0x80 TLV */ rsf_length[0] = (newlen >> 8) & 0xFF; rsf_length[1] = newlen & 0xFF; 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) { 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 val_length = newlen; /* not using sc_asn1_put_tag(...) because rtecp do not support asn1 properly (when val_length > 127) */ *p++ = 0xA5; *p++ = (u8)val_length; 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; newlen -= val_length; if (newlen) apdu.flags |= SC_APDU_FLAGS_CHAINING; } apdu.lc = p - buf; apdu.data = buf; apdu.datalen = p - buf; r = sc_transmit_apdu(card, &apdu); sc_mem_clear(buf, buf_length); free(buf); 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_reset_retry_counter(sc_card_t *card, unsigned int type, int ref_qualifier, const u8 *puk, size_t puklen, const u8 *newref, size_t newlen) { sc_apdu_t apdu; int r; (void)type, (void)puk, (void)puklen; /* no warning */ 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); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(card->ctx, r, "Unblock card failed"); if (newref && newlen) { u8 tmp[2], buf[SC_MAX_APDU_BUFFER_SIZE]; u8 *p = buf; tmp[0] = (newlen >> 8) & 0xFF; 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); 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; apdu.data = buf; apdu.datalen = p - buf; 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, "Set PIN failed"); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } static int rtecp_create_file(sc_card_t *card, sc_file_t *file) { int r; if (!card || !card->ctx || !file) return SC_ERROR_INVALID_ARGUMENTS; if (file->sec_attr_len == 0) { r = set_sec_attr_from_acl(card, file); LOG_TEST_RET(card->ctx, r, "Set sec_attr from ACL failed"); } r = iso_ops->create_file(card, file); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } static int rtecp_list_files(sc_card_t *card, u8 *buf, size_t buflen) { sc_apdu_t apdu; u8 rbuf[SC_MAX_APDU_RESP_SIZE], previd[2]; const u8 *tag; size_t taglen, len = 0; int r; 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 = sizeof(rbuf); r = sc_transmit_apdu(card, &apdu); 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); LOG_TEST_RET(card->ctx, r, ""); if (apdu.resplen <= 2) 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)) LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); memcpy(previd, tag, sizeof(previd)); if (len + sizeof(previd) <= buflen) { memcpy(&buf[len], previd, sizeof(previd)); len += sizeof(previd); } tag = sc_asn1_find_tag(card->ctx, apdu.resp + 2, apdu.resplen - 2, 0x82, &taglen); if (!tag || taglen != 2) 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_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); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); 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, (int)len); } static int rtecp_card_ctl(sc_card_t *card, unsigned long request, void *data) { sc_apdu_t apdu; u8 buf[512]; sc_rtecp_genkey_data_t *genkey_data = data; sc_serial_number_t *serial = data; int r; 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: sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x8A, 0, 0); apdu.cla = 0x80; break; case SC_CARDCTL_RTECP_INIT_END: sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x84, 0x4E, 0x19); apdu.cla = 0x80; break; case SC_CARDCTL_GET_SERIALNR: if (!serial) 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); apdu.le = 256; serial->len = sizeof(serial->value); break; case SC_CARDCTL_RTECP_GENERATE_KEY: if (!genkey_data) 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_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_log(card->ctx, "request = 0x%lx\n", request); LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } 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 && request == SC_CARDCTL_RTECP_GENERATE_KEY) { if (genkey_data->type == SC_ALGORITHM_RSA && genkey_data->u.rsa.modulus_len >= apdu.resplen && genkey_data->u.rsa.exponent_len >= 3) { memcpy(genkey_data->u.rsa.modulus, apdu.resp, apdu.resplen); genkey_data->u.rsa.modulus_len = apdu.resplen; reverse(genkey_data->u.rsa.modulus, genkey_data->u.rsa.modulus_len); memcpy(genkey_data->u.rsa.exponent, "\x01\x00\x01", 3); genkey_data->u.rsa.exponent_len = 3; } else if (genkey_data->type == SC_ALGORITHM_GOSTR3410 && genkey_data->u.gostr3410.xy_len >= apdu.resplen) { memcpy(genkey_data->u.gostr3410.xy, apdu.resp, apdu.resplen); genkey_data->u.gostr3410.xy_len = apdu.resplen; } else r = SC_ERROR_BUFFER_TOO_SMALL; } else if (!r && request == SC_CARDCTL_GET_SERIALNR) { if (apdu.resplen <= sizeof(serial->value)) { memcpy(serial->value, apdu.resp, apdu.resplen); serial->len = apdu.resplen; } else r = SC_ERROR_BUFFER_TOO_SMALL; } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } static int rtecp_construct_fci(sc_card_t *card, const sc_file_t *file, u8 *out, size_t *outlen) { u8 buf[64], *p = out; 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 */ /* 0x80 - Number of data bytes in the file, excluding structural information */ buf[0] = (file->size >> 8) & 0xFF; buf[1] = file->size & 0xFF; sc_asn1_put_tag(0x80, buf, 2, p, *outlen - (p - out), &p); /* 0x82 - File descriptor byte */ if (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); } else { switch (file->type) { case SC_FILE_TYPE_WORKING_EF: buf[0] = 0x01; break; case SC_FILE_TYPE_DF: buf[0] = 0x38; break; case SC_FILE_TYPE_INTERNAL_EF: default: LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } buf[1] = 0; sc_asn1_put_tag(0x82, buf, 2, p, *outlen - (p - out), &p); } /* 0x83 - File identifier */ buf[0] = (file->id >> 8) & 0xFF; buf[1] = file->id & 0xFF; sc_asn1_put_tag(0x83, buf, 2, p, *outlen - (p - out), &p); if (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) { 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); } out[1] = p - out - 2; /* length */ *outlen = p - out; SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, 0); } struct sc_card_driver * sc_get_rtecp_driver(void) { if (iso_ops == NULL) iso_ops = sc_get_iso7816_driver()->ops; rtecp_ops = *iso_ops; rtecp_ops.match_card = rtecp_match_card; rtecp_ops.init = rtecp_init; /* read_binary */ rtecp_ops.write_binary = NULL; /* update_binary */ rtecp_ops.read_record = NULL; rtecp_ops.write_record = NULL; rtecp_ops.append_record = NULL; rtecp_ops.update_record = NULL; rtecp_ops.select_file = rtecp_select_file; /* get_response */ /* get_challenge */ rtecp_ops.verify = rtecp_verify; rtecp_ops.logout = rtecp_logout; /* restore_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; rtecp_ops.reset_retry_counter = rtecp_reset_retry_counter; rtecp_ops.create_file = rtecp_create_file; /* delete_file */ rtecp_ops.list_files = rtecp_list_files; /* check_sw */ rtecp_ops.card_ctl = rtecp_card_ctl; /* process_fci */ rtecp_ops.construct_fci = rtecp_construct_fci; rtecp_ops.pin_cmd = NULL; return &rtecp_drv; } OpenSC-0.26.1/src/libopensc/card-rutoken.c000066400000000000000000001137371474147347300202700ustar00rootroot00000000000000/* * card-rutoken.c: Support for Rutoken S cards * * Copyright (C) 2007 Pavel Mironchik * Copyright (C) 2007 Eugene Hermann * * 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 #include #include "internal.h" #include "opensc.h" #include "pkcs15.h" #include "asn1.h" #include "cardctl.h" struct auth_senv { unsigned int algorithm; }; typedef struct auth_senv auth_senv_t; struct helper_acl_to_sec_attr { unsigned int ac_op; size_t sec_attr_pos; }; typedef struct helper_acl_to_sec_attr helper_acl_to_sec_attr_t; static const helper_acl_to_sec_attr_t arr_convert_attr_df [] = { { SC_AC_OP_CREATE, 0 }, { SC_AC_OP_CREATE, 1 }, { SC_AC_OP_DELETE, 6 } }; static const helper_acl_to_sec_attr_t arr_convert_attr_ef [] = { { SC_AC_OP_READ, 0 }, { SC_AC_OP_UPDATE, 1 }, { SC_AC_OP_WRITE, 1 }, { SC_AC_OP_DELETE, 6 } }; static const sc_SecAttrV2_t default_sec_attr = { 0x42, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0 /* reserve */ }; static const struct sc_card_operations *iso_ops = NULL; static struct sc_card_operations rutoken_ops; static struct sc_card_driver rutoken_drv = { "Rutoken driver", "rutoken", &rutoken_ops, NULL, 0, NULL }; 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); free(card->drv_data); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } 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_log(card->ctx, "ATR recognized as Rutoken\n"); LOG_FUNC_RETURN(card->ctx, 1); } LOG_FUNC_RETURN(card->ctx, 0); } static int token_init(sc_card_t *card, const char *card_name) { LOG_FUNC_CALLED(card->ctx); card->name = card_name; card->caps |= SC_CARD_CAP_RNG; card->drv_data = calloc(1, sizeof(auth_senv_t)); if (card->drv_data == NULL) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_OUT_OF_MEMORY); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); } static int rutoken_init(sc_card_t *card) { int ret; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); /* &rutoken_atrs[1] : { uaToken S ATR, NULL ATR } */ if (_sc_match_atr(card, &rutoken_atrs[1], &card->type) >= 0) ret = token_init(card, "uaToken S card"); else ret = token_init(card, "Rutoken S card"); 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"}, { 0x6400, SC_ERROR_CARD_CMD_FAILED, "Aborting"}, { 0x6500, SC_ERROR_MEMORY_FAILURE, "Memory failure"}, { 0x6581, SC_ERROR_MEMORY_FAILURE, "Memory failure"}, { 0x6700, SC_ERROR_WRONG_LENGTH, "Lc or Le invalid"}, { 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"}, { 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"}, { 0x6C00, SC_ERROR_WRONG_LENGTH, "Le does not fit the data to be sent"}, { 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"}, { 0x9000, SC_SUCCESS, NULL} }; static int rutoken_check_sw(sc_card_t *card, unsigned int sw1, unsigned int sw2) { size_t i; 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_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_log(card->ctx, "Unknown SWs; SW1=%02X, SW2=%02X\n", sw1, sw2); return SC_ERROR_CARD_CMD_FAILED; } static void swap_pair(u8 *buf, size_t len) { size_t i; u8 tmp; for (i = 0; i + 1 < len; i += 2) { tmp = buf[i]; buf[i] = buf[i + 1]; buf[i + 1] = tmp; } } static void swap_four(u8 *buf, size_t len) { size_t i; u8 tmp; for (i = 0; i + 3 < len; i += 4) { tmp = buf[i]; buf[i] = buf[i + 3]; buf[i + 3] = tmp; swap_pair(&buf[i + 1], 2); } } static int rutoken_list_files(sc_card_t *card, u8 *buf, size_t buflen) { sc_apdu_t apdu; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE], previd[2]; const u8 *tag; size_t taglen, len = 0; int ret; assert(card && card->ctx); SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); assert(buf); sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xA4, 0, 0); for (;;) { apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = 256; ret = sc_transmit_apdu(card, &apdu); 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); LOG_TEST_RET(card->ctx, ret, ""); if (apdu.resplen <= 2) 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)) LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); memcpy(previd, tag, sizeof(previd)); if (len + sizeof(previd) <= buflen) { buf[len++] = previd[1]; buf[len++] = previd[0]; } tag = sc_asn1_find_tag(card->ctx, apdu.resp + 2, apdu.resplen - 2, 0x82, &taglen); if (!tag || taglen != 2) 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_2_SHORT, 0xA4, 0x03, 0); apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = 256; ret = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, ret, "APDU transmit failed"); ret = sc_check_sw(card, apdu.sw1, apdu.sw2); 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); } LOG_FUNC_RETURN(card->ctx, (int)len); } static void set_acl_from_sec_attr(sc_card_t *card, sc_file_t *file) { if (file->sec_attr && file->sec_attr_len == sizeof(sc_SecAttrV2_t)) { 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 */ { 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, (int)(*(int8_t*)&file->sec_attr[1 +6]), file->sec_attr[1+7 +6*4]); } if (file->sec_attr[0] & 0x01) /* if AccessMode.0 */ { 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]); sc_file_add_acl_entry(file, (file->type == SC_FILE_TYPE_DF) ? SC_AC_OP_CREATE : SC_AC_OP_READ, (int)(*(int8_t*)&file->sec_attr[1 +0]), file->sec_attr[1+7 +0*4]); } if (file->type == SC_FILE_TYPE_DF) { sc_file_add_acl_entry(file, SC_AC_OP_LIST_FILES, SC_AC_NONE, SC_AC_KEY_REF_NONE); } else if (file->sec_attr[0] & 0x02) /* if AccessMode.1 */ { 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_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, (int)(*(int8_t*)&file->sec_attr[1 +1]), file->sec_attr[1+7 +1*4]); } } } static int rutoken_select_file(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file_out) { sc_apdu_t apdu; u8 buf[SC_MAX_APDU_BUFFER_SIZE], pathbuf[SC_MAX_PATH_SIZE], *path = pathbuf; sc_file_t *file = NULL; size_t pathlen; int ret; assert(card && card->ctx); SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); assert(in_path && sizeof(pathbuf) >= in_path->len); memcpy(path, in_path->value, in_path->len); pathlen = in_path->len; /* p2 = 0; first record, return FCP */ sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0, 0); switch (in_path->type) { case SC_PATH_TYPE_FILE_ID: if (pathlen != 2) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); break; case SC_PATH_TYPE_PATH: if (pathlen >= 2 && memcmp(path, "\x3F\x00", 2) == 0) { if (pathlen == 2) break; /* only 3F00 supplied */ path += 2; pathlen -= 2; } apdu.p1 = 0x08; break; case SC_PATH_TYPE_DF_NAME: case SC_PATH_TYPE_FROM_CURRENT: case SC_PATH_TYPE_PARENT: LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); default: LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } swap_pair(path, pathlen); apdu.lc = pathlen; apdu.data = path; apdu.datalen = pathlen; apdu.resp = buf; apdu.resplen = sizeof(buf); apdu.le = 256; ret = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, ret, "APDU transmit failed"); if (file_out == NULL) { if (apdu.sw1 == 0x61) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, 0); 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); LOG_TEST_RET(card->ctx, ret, ""); if (apdu.resplen > 0 && apdu.resp[0] != 0x62) /* Tag 0x62 - FCP */ LOG_FUNC_RETURN(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); file = sc_file_new(); if (file == NULL) LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); file->path = *in_path; if (card->ops->process_fci == NULL) { sc_file_free(file); 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 (file->sec_attr && file->sec_attr_len == sizeof(sc_SecAttrV2_t)) set_acl_from_sec_attr(card, file); else ret = SC_ERROR_UNKNOWN_DATA_RECEIVED; if (ret != SC_SUCCESS) sc_file_free(file); else { assert(file_out); *file_out = file; } 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, u8 *out, size_t *outlen) { u8 buf[64], *p = out; assert(card && card->ctx); LOG_FUNC_CALLED(card->ctx); assert(file && out && outlen); assert(*outlen >= (size_t)(p - out) + 2); *p++ = 0x62; /* FCP template */ p++; /* for length */ /* 0x80 - Number of data bytes in the file, excluding structural information */ buf[1] = (file->size >> 8) & 0xFF; buf[0] = file->size & 0xFF; sc_asn1_put_tag(0x80, buf, 2, p, *outlen - (p - out), &p); /* 0x82 - File descriptor byte */ if (file->type_attr_len) { assert(sizeof(buf) >= file->type_attr_len); memcpy(buf, file->type_attr, file->type_attr_len); sc_asn1_put_tag(0x82, buf, file->type_attr_len, p, *outlen - (p - out), &p); } else { switch (file->type) { case SC_FILE_TYPE_WORKING_EF: buf[0] = 0x01; break; case SC_FILE_TYPE_DF: buf[0] = 0x38; break; case SC_FILE_TYPE_INTERNAL_EF: default: LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } buf[1] = 0; sc_asn1_put_tag(0x82, buf, 2, p, *outlen - (p - out), &p); } /* 0x83 - File identifier */ buf[1] = (file->id >> 8) & 0xFF; buf[0] = file->id & 0xFF; sc_asn1_put_tag(0x83, buf, 2, p, *outlen - (p - out), &p); if (file->prop_attr_len) { assert(sizeof(buf) >= file->prop_attr_len); 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); memcpy(buf, file->sec_attr, file->sec_attr_len); sc_asn1_put_tag(0x86, buf, file->sec_attr_len, p, *outlen - (p - out), &p); } out[1] = p - out - 2; /* length */ *outlen = p - out; SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, 0); } static int set_sec_attr_from_acl(sc_card_t *card, sc_file_t *file) { const helper_acl_to_sec_attr_t *conv_attr; size_t i, n_conv_attr; const sc_acl_entry_t *entry; sc_SecAttrV2_t attr = { 0 }; int ret = SC_SUCCESS; LOG_FUNC_CALLED(card->ctx); if (file->type == SC_FILE_TYPE_DF) { conv_attr = arr_convert_attr_df; n_conv_attr = sizeof(arr_convert_attr_df)/sizeof(arr_convert_attr_df[0]); } else { conv_attr = arr_convert_attr_ef; n_conv_attr = sizeof(arr_convert_attr_ef)/sizeof(arr_convert_attr_ef[0]); } sc_log(card->ctx, "file->type = %i", file->type); for (i = 0; i < n_conv_attr; ++i) { entry = sc_file_get_acl_entry(file, conv_attr[i].ac_op); if (entry && (entry->method == SC_AC_CHV || entry->method == SC_AC_NONE || entry->method == SC_AC_NEVER) ) { /* AccessMode.[conv_attr[i].sec_attr_pos] */ attr[0] |= 1 << conv_attr[i].sec_attr_pos; 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_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_log(card->ctx, "key_ref %u", (u8)entry->key_ref); } } else { 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; } } ret = sc_file_set_sec_attr(file, attr, sizeof(attr)); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, ret); } static int rutoken_create_file(sc_card_t *card, sc_file_t *file) { int ret; assert(card && card->ctx); SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); assert(file); if (file->sec_attr_len == 0) { ret = set_sec_attr_from_acl(card, file); LOG_TEST_RET(card->ctx, ret, "Set sec_attr from ACL failed"); } assert(iso_ops && iso_ops->create_file); ret = iso_ops->create_file(card, file); LOG_FUNC_RETURN(card->ctx, ret); } static int rutoken_delete_file(sc_card_t *card, const sc_path_t *path) { u8 sbuf[2]; 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)) { 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)) { sbuf[1] = path->value[0]; sbuf[0] = path->value[1]; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE4, 0x00, 0x00); apdu.lc = sizeof(sbuf); apdu.datalen = sizeof(sbuf); apdu.data = sbuf; } else /* No file ID given: means currently selected file */ sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0xE4, 0x00, 0x00); 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, const u8 *data, size_t data_len, int *tries_left) { sc_apdu_t apdu; int ret; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x20, 0x00, ref_qualifier); ret = sc_transmit_apdu(card, &apdu); if (ret == SC_SUCCESS && ((apdu.sw1 == 0x90 && apdu.sw2 == 0x00) || apdu.sw1 == 0x63) ) { /* sw1 == 0x63 - may be already login with other ref_qualifier * sw1 == 0x90 && sw2 == 0x00 - already login with ref_qualifier */ /* RESET ACCESS RIGHTS */ sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x40, 0x00, 0x00); apdu.cla = 0x80; ret = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, ret, "APDU transmit failed"); ret = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(card->ctx, ret, "Reset access rights failed"); } sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x20, 0x00, ref_qualifier); apdu.lc = data_len; apdu.datalen = data_len; apdu.data = data; ret = sc_transmit_apdu(card, &apdu); 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); 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); } LOG_FUNC_RETURN(card->ctx, ret); } static int rutoken_logout(sc_card_t *card) { sc_apdu_t apdu; sc_path_t path; int ret; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_format_path("3F00", &path); ret = rutoken_select_file(card, &path, NULL); 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); LOG_TEST_RET(card->ctx, ret, "APDU transmit failed"); ret = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_FUNC_RETURN(card->ctx, ret); } static int rutoken_change_reference_data(sc_card_t *card, unsigned int type, int ref_qualifier, const u8 *old, size_t oldlen, const u8 *newref, size_t newlen, int *tries_left) { sc_apdu_t apdu; int ret; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (old && oldlen) { ret = rutoken_verify(card, type, ref_qualifier, old, oldlen, tries_left); 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); LOG_TEST_RET(card->ctx, ret, "APDU transmit failed"); ret = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_FUNC_RETURN(card->ctx, ret); } static int rutoken_reset_retry_counter(sc_card_t *card, unsigned int type, int ref_qualifier, const u8 *puk, size_t puklen, const u8 *newref, size_t newlen) { #ifdef FORCE_VERIFY_RUTOKEN int left; #endif sc_apdu_t apdu; int ret; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); #ifdef FORCE_VERIFY_RUTOKEN if (puk && puklen) { ret = rutoken_verify(card, type, ref_qualifier, puk, puklen, &left); 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); LOG_TEST_RET(card->ctx, ret, "APDU transmit failed"); ret = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_FUNC_RETURN(card->ctx, ret); } static int rutoken_restore_security_env(sc_card_t *card, int se_num) { sc_apdu_t apdu; int ret; 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); LOG_TEST_RET(card->ctx, ret, "APDU transmit failed"); ret = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_FUNC_RETURN(card->ctx, ret); } static int rutoken_set_security_env(sc_card_t *card, const sc_security_env_t *env, int se_num) { sc_apdu_t apdu; auth_senv_t *senv; u8 data[3] = { 0x83, 0x01 }; int ret; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (!env) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); senv = (auth_senv_t*)card->drv_data; if (!senv) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); if (env->algorithm != SC_ALGORITHM_GOST) LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); senv->algorithm = SC_ALGORITHM_GOST; if (env->key_ref_len != 1) { 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 */ sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 1, 0); apdu.lc = apdu.datalen = sizeof(data); apdu.data = data; switch (env->operation) { case SC_SEC_OPERATION_AUTHENTICATE: apdu.p2 = 0xA4; break; case SC_SEC_OPERATION_DECIPHER: apdu.p2 = 0xB8; break; case SC_SEC_OPERATION_SIGN: apdu.p2 = 0xAA; break; default: LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } /* set SE */ ret = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, ret, "APDU transmit failed"); ret = sc_check_sw(card, apdu.sw1, apdu.sw2); 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); /* 0x80 - Number of data bytes in the file, excluding structural information */ buf[1] = (hdr->wDOBodyLen >> 8) & 0xFF; buf[0] = hdr->wDOBodyLen & 0xFF; sc_asn1_put_tag(0x80, buf, 2, p, *data_len - (p - data), &p); /* 0x83 - Type and ID */ buf[0] = hdr->OTID.byObjectType; buf[1] = hdr->OTID.byObjectID; sc_asn1_put_tag(0x83, buf, 2, p, *data_len - (p - data), &p); /* 0x85 - Options, Flags and Max count of try */ buf[0] = hdr->OP.byObjectOptions; buf[1] = hdr->OP.byObjectFlags; 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)); 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)); *data_len = p - data; } static int rutoken_key_gen(sc_card_t *card, sc_DOHdrV2_t *pHdr) { u8 data[SC_MAX_APDU_BUFFER_SIZE]; size_t data_len = sizeof(data); sc_apdu_t apdu; int ret; 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_NOCHV_MAX_ID_V2) ) { ret = SC_ERROR_INVALID_ARGUMENTS; } else { pHdr->OP.byObjectTry = 0; rutoken_set_do_hdr(data, &data_len, pHdr); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xda, 0x01, 0x65); apdu.data = data; apdu.datalen = apdu.lc = data_len; ret = sc_transmit_apdu(card, &apdu); 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); } static int rutoken_create_do(sc_card_t *card, sc_DO_V2_t * pDO) { u8 data[SC_MAX_APDU_BUFFER_SIZE]; size_t data_len = sizeof(data); sc_apdu_t apdu; int ret; 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.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_NOCHV_MAX_ID_V2) || ((pDO->HDR.OP.byObjectFlags & SC_RUTOKEN_FLAGS_COMPACT_DO) && (pDO->HDR.wDOBodyLen > SC_RUTOKEN_COMPACT_DO_MAX_LEN)) || (pDO->HDR.wDOBodyLen > SC_RUTOKEN_DO_PART_BODY_LEN) ) { ret = SC_ERROR_INVALID_ARGUMENTS; } else { rutoken_set_do_hdr(data, &data_len, &pDO->HDR); assert(sizeof(data) >= data_len + pDO->HDR.wDOBodyLen + 2); ret = sc_asn1_put_tag(0xA5, pDO->abyDOBody, pDO->HDR.wDOBodyLen, data + data_len, sizeof(data) - data_len, NULL); if (ret == SC_SUCCESS) data_len += pDO->HDR.wDOBodyLen + 2; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xda, 0x01, 0x62); apdu.data = data; apdu.datalen = apdu.lc = data_len; ret = sc_transmit_apdu(card, &apdu); 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); } static int rutoken_get_do_info(sc_card_t *card, sc_DO_INFO_t * pInfo) { u8 data[1]; sc_apdu_t apdu; int ret; LOG_FUNC_CALLED(card->ctx); if ((pInfo->SelType != select_first) && ((pInfo->DoId < SC_RUTOKEN_DO_ALL_MIN_ID) || (pInfo->DoId > SC_RUTOKEN_DO_NOCHV_MAX_ID_V2))) { ret = SC_ERROR_INVALID_ARGUMENTS; } else { sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x30, 0x00, 0x00); apdu.cla = 0x80; apdu.resp = pInfo->pDoData; apdu.resplen = sizeof(pInfo->pDoData); apdu.le = 255; memset(apdu.resp, 0, apdu.resplen); switch(pInfo->SelType) { case select_first: apdu.cse = SC_APDU_CASE_2_SHORT; break; case select_next: apdu.p2 = 0x02; /* fall through */ case select_by_id: data[0] = pInfo->DoId; apdu.data = data; apdu.datalen = sizeof(data); apdu.lc = sizeof(data); break; default: SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); break; } ret = sc_transmit_apdu(card, &apdu); 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); } static int rutoken_delete_do(sc_card_t *card, u8 *pId) { u8 data[1]; sc_apdu_t apdu; int ret; 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; } else { sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xda, 0x01, 0x64); data[0] = *pId; apdu.data = data; apdu.datalen = sizeof(data); apdu.lc = sizeof(data); ret = sc_transmit_apdu(card, &apdu); 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); } /* Both direction GOST cipher */ static int rutoken_cipher_p(sc_card_t *card, const u8 * crgram, size_t crgram_len, u8 * out, size_t outlen, int p1, int p2, int isIV) { u8 buf[248]; /* 248 (cipher_chunk) <= SC_MAX_APDU_BUFFER_SIZE */ size_t len, outlen_tail = outlen; int ret; sc_apdu_t apdu; 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); if (!out) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); if (crgram_len < 16 || ((crgram_len) % 8)) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_WRONG_LENGTH); sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, p1, p2); do { len = (crgram_len > sizeof(buf)) ? sizeof(buf) : crgram_len; apdu.lc = len; apdu.datalen = len; apdu.data = crgram; crgram += len; crgram_len -= len; apdu.cla = (crgram_len == 0) ? 0x00 : 0x10; apdu.le = len; apdu.resplen = len; apdu.resp = buf; ret = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, ret, "APDU transmit failed"); ret = sc_check_sw(card, apdu.sw1, apdu.sw2); if (ret == SC_SUCCESS) { if (isIV) { apdu.resp += 8; apdu.resplen -= 8; isIV = 0; } if (apdu.resplen > outlen_tail) ret = SC_ERROR_BUFFER_TOO_SMALL; else { memcpy(out, apdu.resp, apdu.resplen); out += apdu.resplen; outlen_tail -= apdu.resplen; } } } while (ret == SC_SUCCESS && crgram_len != 0); sc_log(card->ctx, "len out cipher %"SC_FORMAT_LEN_SIZE_T"u\n", outlen - outlen_tail); if (ret == SC_SUCCESS) ret = (outlen_tail == 0) ? (int)outlen : SC_ERROR_WRONG_LENGTH; SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, ret); } /* Launcher for cipher */ 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, ptr->outbuf, ptr->outlen, 0x86, 0x80, 0); else ret = rutoken_cipher_p(card, ptr->inbuf, ptr->inlen, ptr->outbuf, ptr->outlen, 0x80, 0x86, 1); if (ret > 0) { if ((size_t)ret == ptr->outlen) ret = SC_SUCCESS; else ret = SC_ERROR_INTERNAL; /* SC_ERROR_DECRYPT_FAILED; */ } return ret; } 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; size_t len; int ret; sc_apdu_t apdu; 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 { sc_format_apdu(card, &apdu, ilen > signing_chunk ? SC_APDU_CASE_3_SHORT : SC_APDU_CASE_4_SHORT, 0x2A, 0x90, 0x80); len = (ilen > signing_chunk) ? signing_chunk : ilen; apdu.lc = len; apdu.datalen = len; apdu.data = in; in += len; ilen -= len; if (ilen == 0) { apdu.cla = 0x00; apdu.le = olen; apdu.resplen = olen; apdu.resp = out; } else apdu.cla = 0x10; ret = sc_transmit_apdu(card, &apdu); 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, u8 * out, size_t outlen) { int ret; auth_senv_t *senv = (auth_senv_t *)card->drv_data; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (!senv) 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; LOG_FUNC_RETURN(card->ctx, ret); } static int rutoken_get_challenge(sc_card_t *card, u8 *rnd, size_t len) { unsigned char rbuf[32]; 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 rutoken_get_serial(sc_card_t *card, sc_serial_number_t *serial) { sc_apdu_t apdu; int ret; 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); 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); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, ret); } static int rutoken_get_info(sc_card_t *card, void *buff) { sc_apdu_t apdu; u8 rbuf[8]; int ret; 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); 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); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, ret); } static int rutoken_format(sc_card_t *card, int apdu_ins) { int ret; sc_apdu_t apdu; 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); 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); } static int rutoken_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) { int ret = (ptr != NULL /*|| cmd == SC_CARDCTL_ERASE_CARD */ || cmd == SC_CARDCTL_RUTOKEN_FORMAT_INIT || cmd == SC_CARDCTL_RUTOKEN_FORMAT_END ) ? SC_SUCCESS : SC_ERROR_INVALID_ARGUMENTS; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (ret == SC_SUCCESS) { switch (cmd) { case SC_CARDCTL_RUTOKEN_CREATE_DO: ret = rutoken_create_do(card, ptr); break; case SC_CARDCTL_RUTOKEN_GENERATE_KEY_DO: ret = rutoken_key_gen(card, ptr); break; case SC_CARDCTL_RUTOKEN_DELETE_DO: ret = rutoken_delete_do(card, ptr); break; case SC_CARDCTL_RUTOKEN_GET_DO_INFO: ret = rutoken_get_do_info(card, ptr); break; case SC_CARDCTL_GET_SERIALNR: ret = rutoken_get_serial(card, ptr); break; case SC_CARDCTL_RUTOKEN_CHANGE_DO: ret = SC_ERROR_NOT_SUPPORTED; break; case SC_CARDCTL_RUTOKEN_GET_INFO: ret = rutoken_get_info(card, ptr); break; case SC_CARDCTL_RUTOKEN_GOST_ENCIPHER: ret = rutoken_cipher_gost(card, ptr, 1); break; case SC_CARDCTL_RUTOKEN_GOST_DECIPHER: ret = rutoken_cipher_gost(card, ptr, 0); break; /* case SC_CARDCTL_ERASE_CARD: */ case SC_CARDCTL_RUTOKEN_FORMAT_INIT: /* ret = rutoken_format(card, 0x7a); *//* APDU: INIT RUTOKEN */ ret = rutoken_format(card, 0x8a); /* APDU: NEW INIT RUTOKEN */ break; case SC_CARDCTL_RUTOKEN_FORMAT_END: ret = rutoken_format(card, 0x7b); /* APDU: FORMAT END */ break; default: sc_log(card->ctx, "cmd = %lu", cmd); ret = SC_ERROR_NOT_SUPPORTED; break; } } LOG_FUNC_RETURN(card->ctx, ret); } static struct sc_card_driver* get_rutoken_driver(void) { if (iso_ops == NULL) iso_ops = sc_get_iso7816_driver()->ops; rutoken_ops = *iso_ops; rutoken_ops.match_card = rutoken_match_card; rutoken_ops.init = rutoken_init; rutoken_ops.finish = rutoken_finish; /* read_binary */ rutoken_ops.write_binary = NULL; /* update_binary */ rutoken_ops.read_record = NULL; rutoken_ops.write_record = NULL; rutoken_ops.append_record = NULL; rutoken_ops.update_record = NULL; rutoken_ops.select_file = rutoken_select_file; rutoken_ops.get_response = NULL; rutoken_ops.get_challenge = rutoken_get_challenge; rutoken_ops.verify = rutoken_verify; rutoken_ops.logout = rutoken_logout; rutoken_ops.restore_security_env = rutoken_restore_security_env; rutoken_ops.set_security_env = rutoken_set_security_env; rutoken_ops.decipher = NULL; rutoken_ops.compute_signature = rutoken_compute_signature; rutoken_ops.change_reference_data = rutoken_change_reference_data; rutoken_ops.reset_retry_counter = rutoken_reset_retry_counter; rutoken_ops.create_file = rutoken_create_file; rutoken_ops.delete_file = rutoken_delete_file; rutoken_ops.list_files = rutoken_list_files; rutoken_ops.check_sw = rutoken_check_sw; rutoken_ops.card_ctl = rutoken_card_ctl; rutoken_ops.process_fci = rutoken_process_fci; rutoken_ops.construct_fci = rutoken_construct_fci; rutoken_ops.pin_cmd = NULL; return &rutoken_drv; } struct sc_card_driver * sc_get_rutoken_driver(void) { return get_rutoken_driver(); } OpenSC-0.26.1/src/libopensc/card-sc-hsm.c000066400000000000000000001556111474147347300177700ustar00rootroot00000000000000/* * card-sc-hsm.c * * 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 * 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 "asn1.h" #include "cardctl.h" #include "types.h" #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; /* Our operations */ static struct sc_card_operations sc_hsm_ops; /* Our driver description */ static struct sc_card_driver sc_hsm_drv = { "SmartCard-HSM", "sc-hsm", &sc_hsm_ops, NULL, 0, NULL }; /* Our AID */ struct sc_aid sc_hsm_aid = { { 0xE8,0x2B,0x06,0x01,0x04,0x01,0x81,0xC3,0x1F,0x02,0x01 }, 11 }; /* Known ATRs for SmartCard-HSMs */ 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_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_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_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_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_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_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_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_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_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_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_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_GOID, 0, NULL }, {NULL, NULL, NULL, 0, 0, NULL} }; static int sc_hsm_select_file_ex(sc_card_t *card, const sc_path_t *in_path, int forceselect, sc_file_t **file_out) { int rv; 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); sc_file_free(file); return rv; } if ((in_path->type == SC_PATH_TYPE_FILE_ID) && in_path->aid.len) { // Split applet selection and file selection into two separate calls cpath = *in_path; cpath.len = 0; cpath.type = SC_PATH_TYPE_DF_NAME; rv = sc_hsm_select_file_ex(card, &cpath, forceselect, NULL); LOG_TEST_RET(card->ctx, rv, "Could not select SmartCard-HSM application"); if (in_path->len) { cpath = *in_path; cpath.aid.len = 0; rv = sc_hsm_select_file_ex(card, &cpath, forceselect, file_out); } return rv; } // Prevent selection of applet unless this is the first time, selection is forced or the device is not authenticated if (in_path->type == SC_PATH_TYPE_DF_NAME || (in_path->type == SC_PATH_TYPE_PATH && in_path->len == sc_hsm_aid.len && !memcmp(in_path->value, sc_hsm_aid.value, sc_hsm_aid.len)) || (in_path->type == SC_PATH_TYPE_PATH && in_path->len == 0 && 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) { sc_file_free(priv->dffcp); // Cache the FCP returned when selecting the applet sc_file_dup(&priv->dffcp, *file_out); } } else { sc_file_dup(file_out, priv->dffcp); rv = SC_SUCCESS; } return rv; } 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(); if (file == NULL) LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); file->path = *in_path; file->id = 0x3F00; file->type = SC_FILE_TYPE_DF; file->magic = SC_FILE_MAGIC; *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); 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; } } /* 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; } static int sc_hsm_select_file(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file_out) { return sc_hsm_select_file_ex(card, in_path, 0, file_out); } 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, type = 0; sc_file_t *file = NULL; 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, &file); LOG_TEST_RET(card->ctx, r, "Could not select SmartCard-HSM application"); // 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; } /* * Encode 16 hexadecimals of SO-PIN into binary form * Caller must check length of sopin and provide an 8 byte buffer */ static int sc_hsm_encode_sopin(const u8 *sopin, u8 *sopinbin) { int i; unsigned char digit; memset(sopinbin, 0, 8); for (i = 0; i < 16; i++) { *sopinbin <<= 4; digit = *sopin++; if (!isxdigit(digit)) return SC_ERROR_PIN_CODE_INCORRECT; digit = toupper(digit); if (digit >= 'A') digit = digit - 'A' + 10; else digit = digit & 0xF; *sopinbin |= digit & 0xf; if (i & 1) sopinbin++; } return SC_SUCCESS; } 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 }; /* Select MinBioClient */ #ifdef ENABLE_SM sc_sm_stop(card); #endif 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); } static int sc_hsm_soc_change(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left) { sc_apdu_t apdu; sc_path_t path; int r; 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"); /* 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: 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) { 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"); /* verify PUK */ sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x20, 0x00, 0x81); 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 PUK"); /* reset retry counter */ sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x2c, 0x03, 0x00); 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 unblock 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"); return r; } static int sc_hsm_soc_biomatch(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left) { sc_apdu_t apdu; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; int r; 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 */ } /* 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); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); /* now check the status bytes */ r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r == SC_SUCCESS) { LOG_FUNC_RETURN(card->ctx, r); } LOG_FUNC_RETURN(card->ctx, SC_ERROR_PIN_CODE_INCORRECT); } #if defined(ENABLE_SM) && defined(ENABLE_OPENPACE) static int sc_hsm_perform_chip_authentication(sc_card_t *card) { int r, protocol; sc_path_t path; u8 all_certs[1024]; EAC_CTX *ctx = NULL; size_t all_certs_len = sizeof all_certs, left, device_cert_len, issuer_cert_len; const unsigned char *cert = all_certs, *device_cert, *issuer_cert; BUF_MEM *comp_pub_key = NULL; sc_cvc_t cvc_device, cvc_issuer; /* this is only needed to call sc_pkcs15emu_sc_hsm_decode_cvc */ sc_pkcs15_card_t p15card; sc_hsm_private_data_t *priv = (sc_hsm_private_data_t *) card->drv_data; /* we know that sc_pkcs15emu_sc_hsm_decode_cvc does not require anything * else to be initialized than p15card->card */ p15card.card = card; memset(&cvc_device, 0, sizeof(cvc_device)); memset(&cvc_issuer, 0, sizeof(cvc_issuer)); if (priv->EF_C_DevAut && priv->EF_C_DevAut_len) { all_certs_len = priv->EF_C_DevAut_len; cert = priv->EF_C_DevAut; } else { /* get issuer and device certificate from the card */ r = sc_path_set(&path, SC_PATH_TYPE_FILE_ID, (u8 *) "\x2F\x02", 2, 0, 0); if (r < 0) goto err; r = sc_select_file(card, &path, NULL); if (r < 0) goto err; 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; /* save EF_C_DevAut for further use */ cert = realloc(priv->EF_C_DevAut, all_certs_len); if (cert) { memcpy((unsigned char *) cert, all_certs, all_certs_len); priv->EF_C_DevAut = (unsigned char *) cert; priv->EF_C_DevAut_len = all_certs_len; } cert = all_certs; } left = all_certs_len; device_cert = cert; r = sc_pkcs15emu_sc_hsm_decode_cvc(&p15card, &cert, &left, &cvc_device); if (r < 0) goto err; device_cert_len = all_certs_len - left; issuer_cert = cert; r = sc_pkcs15emu_sc_hsm_decode_cvc(&p15card, &cert, &left, &cvc_issuer); if (r < 0) goto err; issuer_cert_len = all_certs_len - device_cert_len - left; ctx = EAC_CTX_new(); if (!ctx) { r = SC_ERROR_INTERNAL; goto err; } /* check all CVCs given of the document's pki */ if (!TA_STEP2_import_certificate(ctx, issuer_cert, issuer_cert_len) || !TA_STEP2_import_certificate(ctx, device_cert, device_cert_len)) { r = SC_ERROR_INTERNAL; goto err; } /* XXX on older JCOPs only NID_id_CA_ECDH_3DES_CBC_CBC may be * supported. The card does not export its capabilities. We hardcode * NID_id_CA_ECDH_AES_CBC_CMAC_128 here, because we don't have the older * cards in production. */ protocol = NID_id_CA_ECDH_AES_CBC_CMAC_128; /* initialize CA domain parameter with the document's public key */ if (!EAC_CTX_init_ca(ctx, protocol, 8)) { r = SC_ERROR_INTERNAL; goto err; } EVP_PKEY_free(ctx->ca_ctx->ka_ctx->key); 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 */ comp_pub_key = TA_STEP3_generate_ephemeral_key(ctx); r = perform_chip_authentication_ex(card, ctx, cvc_device.publicPoint, cvc_device.publicPointlen); err: if (r < 0) EAC_CTX_clear_free(ctx); if (comp_pub_key) BUF_MEM_free(comp_pub_key); sc_pkcs15emu_sc_hsm_free_cvc(&cvc_device); sc_pkcs15emu_sc_hsm_free_cvc(&cvc_issuer); return r; } #else static int sc_hsm_perform_chip_authentication(sc_card_t *card) { return SC_ERROR_NOT_SUPPORTED; } #endif static int sc_hsm_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left) { 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; if (cmd == SC_PIN_CMD_GET_SESSION_PIN) { /* First, perform a standard VERIFY */ data->cmd = SC_PIN_CMD_VERIFY; /* we assign pin2.len to 0 early on so that in case of an error we are * not exiting with an undefined session PIN */ data->pin2.len = 0; } if ((card->caps & SC_CARD_CAP_PROTECTED_AUTHENTICATION_PATH) && (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); } 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); } #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->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, NULL); } 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); } else { if ((data->cmd == SC_PIN_CMD_VERIFY) && (data->pin_reference == 0x88)) { if (data->pin1.len != 16) return SC_ERROR_INVALID_PIN_LENGTH; // Save SO PIN for later use in sc_hsm_init_pin() r = sc_hsm_encode_sopin(data->pin1.data, priv->sopin); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } if ((data->cmd == SC_PIN_CMD_CHANGE) && (data->pin_reference == 0x88)) { if ((data->pin1.len != 16) || (data->pin2.len != 16)) return SC_ERROR_INVALID_PIN_LENGTH; r = sc_hsm_encode_sopin(data->pin1.data, cmdbuff); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_hsm_encode_sopin(data->pin2.data, cmdbuff + 8); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x24, 0x00, data->pin_reference); apdu.data = cmdbuff; apdu.datalen = sizeof(cmdbuff); apdu.lc = 16; apdu.resplen = 0; 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 */ sc_format_apdu(card, &apdu, SC_APDU_CASE_2, 0x20, 0x00, data->pin_reference); apdu.resp = rbuf; apdu.resplen = sizeof rbuf; data->apdu = &apdu; } #endif data->pin1.offset = 5; data->pin2.offset = 5; r = (*iso_ops->pin_cmd)(card, data, tries_left); data->apdu = NULL; } LOG_TEST_RET(card->ctx, r, "Verification failed"); if (cmd == SC_PIN_CMD_GET_SESSION_PIN) { /* reset data->cmd to its original value */ 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_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; apdu.resplen = sizeof recvbuf; apdu.le = 0; if (sc_transmit_apdu(card, &apdu) != SC_SUCCESS || sc_check_sw(card, apdu.sw1, apdu.sw2) != SC_SUCCESS) { sc_log(card->ctx, "Generating session PIN failed"); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } if (data->pin2.data && pin2_len > 0) { if (pin2_len >= apdu.resplen) { memcpy((unsigned char *) data->pin2.data, apdu.resp, apdu.resplen); data->pin2.len = apdu.resplen; } else { sc_log(card->ctx, "Buffer too small for session PIN"); } } } else { sc_log(card->ctx, "Session PIN not supported for this PIN (0x%02X)", data->pin_reference); } } LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } 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); return sc_hsm_select_file_ex(card, &path, 1, NULL); } /* 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) { sc_context_t *ctx = card->ctx; sc_apdu_t apdu; u8 cmdbuff[4]; int r; if (idx > 0xffff) { sc_log(ctx, "invalid EF offset: 0x%X > 0xFFFF", idx); return SC_ERROR_OFFSET_TOO_LARGE; } cmdbuff[0] = 0x54; cmdbuff[1] = 0x02; cmdbuff[2] = (idx >> 8) & 0xFF; cmdbuff[3] = idx & 0xFF; assert(count <= sc_get_max_recv_size(card)); sc_format_apdu(card, &apdu, SC_APDU_CASE_4, 0xB1, 0x00, 0x00); apdu.data = cmdbuff; apdu.datalen = 4; apdu.lc = 4; apdu.le = count; apdu.resplen = count; apdu.resp = buf; 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 != SC_ERROR_FILE_END_REACHED) { LOG_TEST_RET(ctx, r, "Check SW error"); } 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) { sc_context_t *ctx = card->ctx; sc_apdu_t apdu; u8 *cmdbuff, *p; size_t len; int r; if (idx > 0xffff) { sc_log(ctx, "invalid EF offset: 0x%X > 0xFFFF", idx); return SC_ERROR_OFFSET_TOO_LARGE; } cmdbuff = malloc(8 + count); if (!cmdbuff) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); } 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+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; 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"); bytes_left -= to_send; offset += to_send; file_offset += to_send; } while (0 < bytes_left); err: free(cmdbuff); LOG_FUNC_RETURN(ctx, (int)count); } static int sc_hsm_update_binary(sc_card_t *card, unsigned int idx, const u8 *buf, size_t count, unsigned long flags) { return sc_hsm_write_ef(card, 0, idx, buf, count); } static int sc_hsm_list_files(sc_card_t *card, u8 * buf, size_t buflen) { sc_apdu_t apdu; u8 recvbuf[MAX_EXT_APDU_LENGTH]; sc_hsm_private_data_t *priv = (sc_hsm_private_data_t *) card->drv_data; int r; if (priv->noExtLength) { sc_format_apdu(card, &apdu, SC_APDU_CASE_2, 0x58, 0, 0); } else { sc_format_apdu(card, &apdu, SC_APDU_CASE_2_EXT, 0x58, 0, 0); } apdu.cla = 0x80; apdu.resp = recvbuf; apdu.resplen = sizeof(recvbuf); apdu.le = 0; r = sc_transmit_apdu(card, &apdu); if ((r == SC_ERROR_TRANSMIT_FAILED) && (!priv->noExtLength)) { sc_log(card->ctx, "No extended length support ? Trying fall-back to short APDUs, probably breaking support for RSA 2048 operations"); priv->noExtLength = 1; card->max_send_size = 248; // 255 - 7 because of TLV in odd ins UPDATE BINARY return sc_hsm_list_files(card, buf, buflen); } LOG_TEST_RET(card->ctx, r, "ENUMERATE OBJECTS APDU transmit failed"); if (buflen < apdu.resplen) memcpy(buf, recvbuf, buflen); else memcpy(buf, recvbuf, apdu.resplen); LOG_FUNC_RETURN(card->ctx, (int)apdu.resplen); } static int sc_hsm_create_file(sc_card_t *card, sc_file_t *file) { int r; r = sc_hsm_write_ef(card, file->id, 0, NULL, 0); LOG_TEST_RET(card->ctx, r, "Create file failed"); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } static int sc_hsm_delete_file(sc_card_t *card, const sc_path_t *path) { sc_context_t *ctx = card->ctx; sc_apdu_t apdu; u8 sbuf[2]; int r; if ((path->type != SC_PATH_TYPE_FILE_ID) || (path->len != 2)) { sc_log(card->ctx, "File type has to be SC_PATH_TYPE_FILE_ID"); 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, 0x02, 0x00); apdu.data = sbuf; apdu.datalen = sizeof(sbuf); apdu.lc = sizeof(sbuf); 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"); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int sc_hsm_set_security_env(sc_card_t *card, const sc_security_env_t *env, int se_num) { sc_hsm_private_data_t *priv = (sc_hsm_private_data_t *) card->drv_data; priv->env = env; switch(env->algorithm) { case SC_ALGORITHM_RSA: if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1) { if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA1) { 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; } else { priv->algorithm = ALGO_RSA_RAW; } } break; case SC_ALGORITHM_EC: 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; } else if (env->algorithm_flags & SC_ALGORITHM_ECDSA_HASH_SHA224) { 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) { priv->algorithm = ALGO_EC_RAW; } else { LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } break; default: LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); break; } LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } static int sc_hsm_decode_ecdsa_signature(sc_card_t *card, const u8 * data, size_t datalen, 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); r = sc_asn1_decode_ecdsa_signature(card->ctx, data, datalen, fieldsizebytes, &out, outlen); LOG_FUNC_RETURN(card->ctx, r); } static int sc_hsm_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[514]; sc_hsm_private_data_t *priv; if (card == NULL || data == NULL || out == NULL) { return SC_ERROR_INVALID_ARGUMENTS; } priv = (sc_hsm_private_data_t *) card->drv_data; if (priv->env == NULL) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_OBJECT_NOT_FOUND); } 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 = 512; apdu.data = data; apdu.lc = datalen; apdu.datalen = datalen; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { int len; if ((priv->algorithm & 0xF0) == ALGO_EC_RAW) { 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 = (int)(apdu.resplen > outlen ? outlen : apdu.resplen); memcpy(out, apdu.resp, len); } LOG_FUNC_RETURN(card->ctx, len); } LOG_FUNC_RETURN(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2)); } static int sc_hsm_decipher(sc_card_t *card, const u8 * crgram, size_t crgram_len, u8 * out, size_t outlen) { int r; size_t len; sc_apdu_t apdu; u8 rbuf[514]; sc_hsm_private_data_t *priv; if (card == NULL || crgram == NULL || out == NULL) { return SC_ERROR_INVALID_ARGUMENTS; } LOG_FUNC_CALLED(card->ctx); priv = (sc_hsm_private_data_t *) card->drv_data; 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 = 512; apdu.data = (u8 *)crgram; apdu.lc = crgram_len; apdu.datalen = crgram_len; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { if (priv->algorithm == ALGO_EC_DH) { // // The SmartCard-HSM returns the point result of the DH operation // with a leading '04' assert(apdu.resplen > 0); len = apdu.resplen - 1 > outlen ? outlen : apdu.resplen - 1; memcpy(out, apdu.resp + 1, 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, (int)len); } } else LOG_FUNC_RETURN(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2)); } void sc_hsm_set_serialnr(sc_card_t *card, char *serial) { sc_hsm_private_data_t *priv = (sc_hsm_private_data_t *) card->drv_data; if (priv->serialno) { free(priv->serialno); } priv->serialno = strdup(serial); } static int sc_hsm_get_serialnr(sc_card_t *card, sc_serial_number_t *serial) { sc_hsm_private_data_t *priv = (sc_hsm_private_data_t *) card->drv_data; 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; } serial->len = strlen(priv->serialno); if (serial->len > sizeof(serial->value)) serial->len = sizeof(serial->value); memcpy(serial->value, priv->serialno, serial->len); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } static int sc_hsm_initialize(sc_card_t *card, sc_cardctl_sc_hsm_init_param_t *params) { sc_context_t *ctx = card->ctx; sc_pkcs15_tokeninfo_t ti; struct sc_pin_cmd_data pincmd; int r; size_t tilen; sc_apdu_t apdu; u8 ibuff[68+0xFF], *p; LOG_FUNC_CALLED(card->ctx); p = ibuff; *p++ = 0x80; // Options *p++ = 0x02; memcpy(p, params->options, 2); p += 2; if (params->user_pin_len > 0xFF) { return SC_ERROR_INVALID_ARGUMENTS; } *p++ = 0x81; // User PIN *p++ = (u8)params->user_pin_len; memcpy(p, params->user_pin, params->user_pin_len); p += params->user_pin_len; *p++ = 0x82; // Initialization code *p++ = 0x08; memcpy(p, params->init_code, 8); p += 8; *p++ = 0x91; // User PIN retry counter *p++ = 0x01; *p++ = params->user_pin_retry_counter; if (params->dkek_shares >= 0) { *p++ = 0x92; // Number of DKEK shares *p++ = 0x01; *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; apdu.datalen = p - ibuff; apdu.lc = 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); if (r == SC_ERROR_NOT_ALLOWED) { r = SC_ERROR_PIN_CODE_INCORRECT; } LOG_TEST_RET(ctx, r, "Check SW error"); if (params->label) { memset(&ti, 0, sizeof(ti)); ti.label = params->label; ti.flags = SC_PKCS15_TOKEN_PRN_GENERATION; r = sc_pkcs15_encode_tokeninfo(ctx, &ti, &p, &tilen); LOG_TEST_RET(ctx, r, "Error encoding tokeninfo"); memset(&pincmd, 0, sizeof(pincmd)); pincmd.cmd = SC_PIN_CMD_VERIFY; pincmd.pin_type = SC_AC_CHV; pincmd.pin_reference = 0x81; pincmd.pin1.data = params->user_pin; pincmd.pin1.len = params->user_pin_len; r = (*iso_ops->pin_cmd)(card, &pincmd, NULL); LOG_TEST_RET(ctx, r, "Could not verify PIN"); r = sc_hsm_write_ef(card, 0x2F03, 0, p, tilen); LOG_TEST_RET(ctx, r, "Could not write EF.TokenInfo"); } LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } static int sc_hsm_import_dkek_share(sc_card_t *card, sc_cardctl_sc_hsm_dkek_t *params) { sc_context_t *ctx = card->ctx; sc_apdu_t apdu; u8 status[SC_MAX_APDU_BUFFER_SIZE]; int r; LOG_FUNC_CALLED(card->ctx); if (params->importShare) { sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x52, 0x00, 0x00); apdu.cla = 0x80; apdu.data = params->dkek_share; apdu.datalen = sizeof(params->dkek_share); apdu.lc = apdu.datalen; } else { sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x52, 0x00, 0x00); } apdu.cla = 0x80; apdu.le = 0; apdu.resp = status; apdu.resplen = sizeof(status); 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"); assert(apdu.resplen >= (sizeof(params->key_check_value) + 2)); params->dkek_shares = status[0]; params->outstanding_shares = status[1]; memcpy(params->key_check_value, status + 2, sizeof(params->key_check_value)); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } static int sc_hsm_wrap_key(sc_card_t *card, sc_cardctl_sc_hsm_wrapped_key_t *params) { sc_context_t *ctx = card->ctx; sc_apdu_t apdu; u8 data[1500]; int r; LOG_FUNC_CALLED(card->ctx); sc_format_apdu(card, &apdu, SC_APDU_CASE_2_EXT, 0x72, params->key_id, 0x92); apdu.cla = 0x80; apdu.le = 0; apdu.resp = data; apdu.resplen = sizeof(data); 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"); if (params->wrapped_key == NULL) { params->wrapped_key_length = apdu.resplen; params->wrapped_key = malloc(apdu.resplen); if (params->wrapped_key == NULL) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); } } else { if (apdu.resplen > params->wrapped_key_length) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_BUFFER_TOO_SMALL); } params->wrapped_key_length = apdu.resplen; } memcpy(params->wrapped_key, data, apdu.resplen); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } static int sc_hsm_unwrap_key(sc_card_t *card, sc_cardctl_sc_hsm_wrapped_key_t *params) { sc_context_t *ctx = card->ctx; sc_apdu_t apdu; int r; LOG_FUNC_CALLED(card->ctx); 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); 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); } static int sc_hsm_init_token(sc_card_t *card, sc_cardctl_pkcs11_init_token_t *params) { sc_context_t *ctx = card->ctx; sc_cardctl_sc_hsm_init_param_t ip; int r; char label[33],*cpo; LOG_FUNC_CALLED(ctx); if (params->so_pin_len != 16) { LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "SO PIN wrong length (!=16)"); } memset(&ip, 0, sizeof(ip)); ip.dkek_shares = -1; ip.options[0] = 0x00; ip.options[1] = 0x01; r = sc_hsm_encode_sopin(params->so_pin, ip.init_code); LOG_TEST_RET(ctx, r, "SO PIN wrong format"); ip.user_pin = ip.init_code; // Use the first 6 bytes of the SO-PIN as initial User-PIN value ip.user_pin_len = 6; ip.user_pin_retry_counter = 3; if (params->label) { // Strip trailing spaces memcpy(label, params->label, 32); label[32] = 0; cpo = label + 31; while ((cpo >= label) && (*cpo == ' ')) { *cpo = 0; cpo--; } ip.label = label; } r = sc_hsm_initialize(card, &ip); LOG_TEST_RET(ctx, r, "Check SW error"); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int sc_hsm_init_pin(sc_card_t *card, sc_cardctl_pkcs11_init_pin_t *params) { sc_hsm_private_data_t *priv = (sc_hsm_private_data_t *) card->drv_data; sc_context_t *ctx = card->ctx; int r; sc_apdu_t apdu; u8 ibuff[50], *p; LOG_FUNC_CALLED(card->ctx); if (params->pin_len > 16) { LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "User PIN too long"); } p = ibuff; memcpy(p, priv->sopin, sizeof(priv->sopin)); p += sizeof(priv->sopin); memcpy(p, params->pin, params->pin_len); p += params->pin_len; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x2C, 0x00, 0x81); apdu.data = ibuff; apdu.datalen = p - ibuff; apdu.lc = 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); // Cards before version 1.0 do not implement RESET_RETRY_COUNTER // For those cards the CHANGE REFERENCE DATA command is used instead if (r == SC_ERROR_INS_NOT_SUPPORTED) { p = ibuff; memcpy(p, priv->sopin, 6); p += 6; memcpy(p, params->pin, params->pin_len); p += params->pin_len; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x24, 0x00, 0x81); apdu.data = ibuff; apdu.datalen = p - ibuff; apdu.lc = 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, "Check SW error"); memset(priv->sopin, 0, sizeof(priv->sopin)); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } static int sc_hsm_generate_keypair(sc_card_t *card, sc_cardctl_sc_hsm_keygen_info_t *keyinfo) { u8 rbuf[1200]; int r; sc_apdu_t apdu; LOG_FUNC_CALLED(card->ctx); sc_format_apdu(card, &apdu, SC_APDU_CASE_4_EXT, 0x46, keyinfo->key_id, keyinfo->auth_key_id); apdu.cla = 0x00; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = 0; apdu.data = keyinfo->gakprequest; apdu.lc = keyinfo->gakprequest_len; apdu.datalen = keyinfo->gakprequest_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); LOG_TEST_RET(card->ctx, r, "Check SW error"); keyinfo->gakpresponse_len = apdu.resplen; keyinfo->gakpresponse = malloc(apdu.resplen); if (keyinfo->gakpresponse == NULL) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); } memcpy(keyinfo->gakpresponse, apdu.resp, apdu.resplen); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } static int sc_hsm_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) { switch (cmd) { case SC_CARDCTL_GET_SERIALNR: return sc_hsm_get_serialnr(card, (sc_serial_number_t *)ptr); case SC_CARDCTL_PKCS11_INIT_TOKEN: return sc_hsm_init_token(card, (sc_cardctl_pkcs11_init_token_t *)ptr); case SC_CARDCTL_PKCS11_INIT_PIN: return sc_hsm_init_pin(card, (sc_cardctl_pkcs11_init_pin_t *)ptr); case SC_CARDCTL_SC_HSM_GENERATE_KEY: return sc_hsm_generate_keypair(card, (sc_cardctl_sc_hsm_keygen_info_t *)ptr); case SC_CARDCTL_SC_HSM_INITIALIZE: return sc_hsm_initialize(card, (sc_cardctl_sc_hsm_init_param_t *)ptr); case SC_CARDCTL_SC_HSM_IMPORT_DKEK_SHARE: return sc_hsm_import_dkek_share(card, (sc_cardctl_sc_hsm_dkek_t *)ptr); case SC_CARDCTL_SC_HSM_WRAP_KEY: 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; } static int sc_hsm_init(struct sc_card *card) { #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 = NULL; sc_path_t path; sc_hsm_private_data_t *priv = NULL; LOG_FUNC_CALLED(card->ctx); 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| SC_ALGORITHM_ECDSA_HASH_NONE| 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| SC_ALGORITHM_EXT_EC_ECPARAMETERS| SC_ALGORITHM_EXT_EC_NAMEDCURVE| SC_ALGORITHM_EXT_EC_UNCOMPRESES| SC_ALGORITHM_ONBOARD_KEY_GEN; _sc_card_add_ec_alg(card, 192, flags, ext_flags, NULL); _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 Buffer limits // JCOP 2.4.1r3 1462 // JCOP 2.4.2r3 1454 // JCOP 3 1232 // MicroSD with JCOP 3 478 / 506 - handled in reader-pcsc.c // Reiner SCT 1014 - handled in reader-pcsc.c // Use JCOP 3 card limits for sending card->max_send_size = 1232; // 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 && file->prop_attr && file->prop_attr_len >= 2) { static char card_name[SC_MAX_APDU_BUFFER_SIZE]; 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; break; case 0x01: p = p01; break; default: break; } snprintf(card_name, sizeof card_name, "%s version %u.%u", p, major, minor); card->name = card_name; if (file->prop_attr[1] & 0x04) { card->caps |= SC_CARD_CAP_SESSION_PIN; } } sc_file_free(file); priv->EF_C_DevAut = NULL; priv->EF_C_DevAut_len = 0; #if defined(ENABLE_SM) && defined(ENABLE_OPENPACE) EAC_init(); #ifdef _WIN32 expanded_len = ExpandEnvironmentStringsA(CVCDIR, expanded_val, sizeof expanded_val); if (0 < expanded_len && expanded_len < sizeof expanded_val) EAC_set_cvc_default_dir(expanded_val); #else EAC_set_cvc_default_dir(CVCDIR); #endif #endif return 0; } 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); #endif if (priv) { free(priv->serialno); sc_file_free(priv->dffcp); free(priv->EF_C_DevAut); } free(priv); return SC_SUCCESS; } 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; 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; sc_hsm_ops.create_file = sc_hsm_create_file; sc_hsm_ops.delete_file = sc_hsm_delete_file; sc_hsm_ops.set_security_env = sc_hsm_set_security_env; sc_hsm_ops.compute_signature = sc_hsm_compute_signature; sc_hsm_ops.decipher = sc_hsm_decipher; sc_hsm_ops.init = sc_hsm_init; sc_hsm_ops.finish = sc_hsm_finish; sc_hsm_ops.card_ctl = sc_hsm_card_ctl; sc_hsm_ops.pin_cmd = sc_hsm_pin_cmd; sc_hsm_ops.logout = sc_hsm_logout; /* no record oriented file services */ sc_hsm_ops.read_record = NULL; sc_hsm_ops.write_record = NULL; sc_hsm_ops.append_record = NULL; sc_hsm_ops.update_record = NULL; return &sc_hsm_drv; } struct sc_card_driver * sc_get_sc_hsm_driver(void) { return sc_get_driver(); } OpenSC-0.26.1/src/libopensc/card-sc-hsm.h000066400000000000000000000144411474147347300177700ustar00rootroot00000000000000/* * sc-hsm.h * * Copyright (C) 2012 Andreas Schwier, CardContact, Minden, Germany * * 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_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 */ #define CD_PREFIX 0xC8 /* Hi byte in file identifier for PKCS#15 CD objects */ #define DCOD_PREFIX 0xC9 /* Hi byte in file identifier for PKCS#15 DCOD objects */ #define CA_CERTIFICATE_PREFIX 0xCA /* Hi byte in file identifier for CA certificates */ #define KEY_PREFIX 0xCC /* Hi byte in file identifier for key objects */ #define PROT_DATA_PREFIX 0xCD /* Hi byte in file identifier for PIN protected data objects */ #define EE_CERTIFICATE_PREFIX 0xCE /* Hi byte in file identifier for EE certificates */ #define DATA_PREFIX 0xCF /* Hi byte in file identifier for readable data objects */ #define ALGO_RSA_RAW 0x20 /* RSA signature with external padding */ #define ALGO_RSA_DECRYPT 0x21 /* RSA decrypt */ #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 */ #define ID_SO_PIN 0x88 /* Security officer PIN identifier */ #define INIT_RRC_ENABLED 0x01 /* Bit 1 of initialization options */ #define INIT_TRANSPORT_PIN 0x02 /* Bit 2 of initialization options */ /* Information the driver maintains between calls */ typedef struct sc_hsm_private_data { const sc_security_env_t *env; sc_file_t *dffcp; u8 algorithm; int noExtLength; char *serialno; u8 sopin[8]; u8 *EF_C_DevAut; size_t EF_C_DevAut_len; } sc_hsm_private_data_t; 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 size_t primeOrModuluslen; u8 *coefficientAorExponent; // Coefficient A for ECC or public exponent for RSA size_t coefficientAorExponentlen; u8 *coefficientB; // Coefficient B for ECC size_t coefficientBlen; u8 *basePointG; // Base point for ECC size_t basePointGlen; u8 *order; // Order of the base point for ECC size_t orderlen; u8 *publicPoint; // Public point for ECC size_t publicPointlen; u8 *cofactor; // Cofactor for ECC size_t cofactorlen; 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; }; typedef struct sc_cvc sc_cvc_t; struct ec_curve { const struct sc_lv_data oid; const struct sc_lv_data prime; const struct sc_lv_data coefficientA; const struct sc_lv_data coefficientB; const struct sc_lv_data basePointG; const struct sc_lv_data order; 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_ */ OpenSC-0.26.1/src/libopensc/card-setcos.c000066400000000000000000001025251474147347300200720ustar00rootroot00000000000000/* * card-setcos.c: Support for PKI cards by Setec * * Copyright (C) 2001, 2002 Juha Yrjölä * Copyright (C) 2005 Antti Tapaninen * Copyright (C) 2005 Zetes * * 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 "asn1.h" #include "cardctl.h" #define _FINEID_BROKEN_SELECT_FLAG 1 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 }, /* 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 } }; #define SETCOS_IS_EID_APPLET(card) ((card)->type == SC_CARD_TYPE_SETCOS_EID_V2_0 || (card)->type == SC_CARD_TYPE_SETCOS_EID_V2_1) /* Setcos 4.4 Life Cycle Status Integer */ #define SETEC_LCSI_CREATE 0x01 #define SETEC_LCSI_INIT 0x03 #define SETEC_LCSI_ACTIVATED 0x07 #define SETEC_LCSI_DEACTIVATE 0x06 #define SETEC_LCSI_TEMINATE 0x0F /* MF only */ static struct sc_card_operations setcos_ops; static struct sc_card_driver setcos_drv = { "Setec cards", "setcos", &setcos_ops, NULL, 0, NULL }; static int match_hist_bytes(sc_card_t *card, const char *str, size_t len) { const char *src = (const char *) card->reader->atr_info.hist_bytes; size_t srclen = card->reader->atr_info.hist_bytes_len; size_t offset = 0; if (len == 0) len = strlen(str); if (srclen < len) return 0; while (srclen - offset > len) { if (memcmp(src + offset, str, len) == 0) { return 1; } offset++; } return 0; } static int setcos_match_card(sc_card_t *card) { sc_apdu_t apdu; u8 buf[6]; int i; 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; } /* Check if it's a EID2.x applet by reading the version info */ sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xCA, 0xDF, 0x30); apdu.cla = 0x00; apdu.resp = buf; apdu.resplen = 5; apdu.le = 5; i = sc_transmit_apdu(card, &apdu); if (i == 0 && apdu.sw1 == 0x90 && apdu.sw2 == 0x00 && apdu.resplen == 5) { if (memcmp(buf, "v2.0", 4) == 0) card->type = SC_CARD_TYPE_SETCOS_EID_V2_0; else if (memcmp(buf, "v2.1", 4) == 0) card->type = SC_CARD_TYPE_SETCOS_EID_V2_1; else { buf[sizeof(buf) - 1] = '\0'; sc_log(card->ctx, "SetCOS EID applet %s is not supported", (char *) buf); return 0; } return 1; } return 0; } card->flags = setcos_atrs[i].flags; return 1; } static int select_pkcs15_app(sc_card_t * card) { sc_path_t app; int r; /* Regular PKCS#15 AID */ sc_format_path("A000000063504B43532D3135", &app); app.type = SC_PATH_TYPE_DF_NAME; r = sc_select_file(card, &app, NULL); return r; } static int setcos_init(sc_card_t *card) { card->name = "SetCOS"; /* Handle unknown or forced cards */ if (card->type < 0) { card->type = SC_CARD_TYPE_SETCOS_GENERIC; } 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); if (card->flags & SC_CARD_FLAG_RNG) card->caps |= SC_CARD_CAP_RNG; break; case SC_CARD_TYPE_SETCOS_44: case SC_CARD_TYPE_SETCOS_EID_V2_0: case SC_CARD_TYPE_SETCOS_EID_V2_1: card->cla = 0x00; card->caps |= SC_CARD_CAP_USE_FCI_AC; card->caps |= SC_CARD_CAP_RNG; card->caps |= SC_CARD_CAP_APDU_EXT; break; default: /* XXX: Get SetCOS version */ card->cla = 0x80; /* SetCOS 4.3.x */ /* State that we have an RNG */ card->caps |= SC_CARD_CAP_RNG; break; } switch (card->type) { case SC_CARD_TYPE_SETCOS_PKI: case SC_CARD_TYPE_SETCOS_FINEID_V2_2048: { 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); _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: case SC_CARD_TYPE_SETCOS_EID_V2_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; 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); } break; } return 0; } static const struct sc_card_operations *iso_ops = NULL; static int setcos_construct_fci_44(sc_card_t *card, const sc_file_t *file, u8 *out, size_t *outlen) { u8 *p = out; u8 buf[64]; const u8 *pin_key_info; int len; /* Command */ *p++ = 0x6F; p++; /* 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_WORKING_EF && file->ef_structure == 0x22))) buf[0] = buf[1] = 0x00; else { buf[0] = (file->size >> 8) & 0xFF; buf[1] = file->size & 0xFF; } sc_asn1_put_tag(0x81, buf, 2, p, *outlen - (p - out), &p); /* Type */ if (file->type_attr_len) { memcpy(buf, file->type_attr, file->type_attr_len); sc_asn1_put_tag(0x82, buf, file->type_attr_len, p, *outlen - (p - out), &p); } else { u8 bLen = 1; buf[0] = file->shareable ? 0x40 : 0; switch (file->type) { case SC_FILE_TYPE_INTERNAL_EF: /* RSA keyfile */ buf[0] = 0x11; break; case SC_FILE_TYPE_WORKING_EF: if (file->ef_structure == 0x22) { /* pin-file */ buf[0] = 0x0A; /* EF linear fixed EF for ISF keys */ if (SETCOS_IS_EID_APPLET(card)) bLen = 1; else { /* Setcos V4.4 */ bLen = 5; buf[1] = 0x41; /* fixed */ buf[2] = file->record_length >> 8; /* 2 byte record length */ buf[3] = file->record_length & 0xFF; buf[4] = file->size / file->record_length; /* record count */ } } else { buf[0] |= file->ef_structure & 7; /* set file-type, only for EF, not for DF objects */ } break; case SC_FILE_TYPE_DF: buf[0] = 0x38; break; default: return SC_ERROR_NOT_SUPPORTED; } sc_asn1_put_tag(0x82, buf, bLen, p, *outlen - (p - out), &p); } /* File ID */ buf[0] = (file->id >> 8) & 0xFF; buf[1] = file->id & 0xFF; sc_asn1_put_tag(0x83, buf, 2, p, *outlen - (p - out), &p); /* DF name */ if (file->type == SC_FILE_TYPE_DF) { if (file->name[0] != 0) sc_asn1_put_tag(0x84, (u8 *) file->name, file->namelen, p, *outlen - (p - out), &p); else { /* Name required -> take the FID if not specified */ buf[0] = (file->id >> 8) & 0xFF; buf[1] = file->id & 0xFF; sc_asn1_put_tag(0x84, buf, 2, p, *outlen - (p - out), &p); } } /* Security Attributes */ memcpy(buf, file->sec_attr, file->sec_attr_len); sc_asn1_put_tag(0x86, buf, file->sec_attr_len, p, *outlen - (p - out), &p); /* Life cycle status */ if (file->prop_attr_len) { memcpy(buf, file->prop_attr, file->prop_attr_len); sc_asn1_put_tag(0x8A, buf, file->prop_attr_len, p, *outlen - (p - out), &p); } /* PIN definitions */ if (file->type == SC_FILE_TYPE_DF) { if (card->type == SC_CARD_TYPE_SETCOS_EID_V2_1) { pin_key_info = (const u8*)"\xC1\x04\x81\x82\x83\x84"; len = 6; } else if (card->type == SC_CARD_TYPE_SETCOS_EID_V2_0) { pin_key_info = (const u8*)"\xC1\x04\x81\x82"; /* Max 2 PINs supported */ len = 4; } else { /* Pin/key info: define 4 pins, no keys */ if(file->path.len == 2) pin_key_info = (const u8*)"\xC1\x04\x81\x82\x83\x84\xC2\x00"; /* root-MF: use local pin-file */ else pin_key_info = (const u8 *)"\xC1\x04\x01\x02\x03\x04\xC2\x00"; /* sub-DF: use parent pin-file in root-MF */ len = 8; } sc_asn1_put_tag(0xA5, pin_key_info, len, p, *outlen - (p - out), &p); } /* Length */ out[1] = p - out - 2; *outlen = p - out; return 0; } 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 || card->type == SC_CARD_TYPE_SETCOS_NIDEL || SETCOS_IS_EID_APPLET(card)) return setcos_construct_fci_44(card, file, out, outlen); else return iso_ops->construct_fci(card, file, out, outlen); } static u8 acl_to_byte(const sc_acl_entry_t *e) { switch (e->method) { case SC_AC_NONE: return 0x00; case SC_AC_CHV: switch (e->key_ref) { case 1: return 0x01; break; case 2: return 0x02; break; default: return 0x00; } break; case SC_AC_TERM: return 0x04; case SC_AC_NEVER: return 0x0F; } return 0x00; } static unsigned int acl_to_byte_44(const struct sc_acl_entry *e, u8* p_bNumber) { /* Handle special fixed values */ if (e == (sc_acl_entry_t *) 1) /* SC_AC_NEVER */ return SC_AC_NEVER; else if ((e == (sc_acl_entry_t *) 2) || /* SC_AC_NONE */ (e == (sc_acl_entry_t *) 3) || /* SC_AC_UNKNOWN */ (e == (sc_acl_entry_t *) 0)) return SC_AC_NONE; /* Handle standard values */ *p_bNumber = e->key_ref; return(e->method); } /* If pin is present in the pins list, return it's index. * If it's not yet present, add it to the list and return the index. */ static int setcos_pin_index_44(int *pins, int len, int pin) { int i; for (i = 0; i < len; i++) { if (pins[i] == pin) return i; if (pins[i] == -1) { pins[i] = pin; return i; } } assert(i != len); /* Too much PINs, shouldn't happen */ return 0; } /* 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) { const u8 bFileStatus = file->status == SC_FILE_STATUS_CREATION ? SETEC_LCSI_CREATE : SETEC_LCSI_ACTIVATED; u8 bCommands_always = 0; int pins[] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; u8 bCommands_pin[sizeof(pins)/sizeof(pins[0])]; /* both 7 entries big */ u8 bCommands_key = 0; u8 bNumber = 0; u8 bKeyNumber = 0; unsigned int bMethod = 0; /* -1 means RFU */ const int df_idx[8] = { /* byte 1 = OpenSC type of AC Bit0, byte 2 = OpenSC type of AC Bit1 ...*/ SC_AC_OP_DELETE, SC_AC_OP_CREATE, SC_AC_OP_CREATE, SC_AC_OP_INVALIDATE, SC_AC_OP_REHABILITATE, SC_AC_OP_LOCK, SC_AC_OP_DELETE, -1}; const int ef_idx[8] = { /* note: SC_AC_OP_SELECT to be ignored, actually RFU */ SC_AC_OP_READ, SC_AC_OP_UPDATE, SC_AC_OP_WRITE, SC_AC_OP_INVALIDATE, SC_AC_OP_REHABILITATE, -1, SC_AC_OP_ERASE, -1}; const int efi_idx[8] = { /* internal EF used for RSA keys */ SC_AC_OP_READ, SC_AC_OP_ERASE, SC_AC_OP_UPDATE, SC_AC_OP_INVALIDATE, SC_AC_OP_REHABILITATE, -1, SC_AC_OP_ERASE, -1}; /* Set file creation status */ sc_file_set_prop_attr(file, &bFileStatus, 1); /* Build ACL from local structure = get AC for each operation group */ if (file->sec_attr_len == 0) { const int* p_idx; int i; int len = 0; u8 bBuf[64]; /* Get specific operation groups for specified file-type */ switch (file->type){ case SC_FILE_TYPE_DF: /* DF */ p_idx = df_idx; break; case SC_FILE_TYPE_INTERNAL_EF: /* EF for RSA keys */ p_idx = efi_idx; break; default: /* SC_FILE_TYPE_WORKING_EF */ p_idx = ef_idx; break; } /* Get enabled commands + required Keys/Pins */ memset(bCommands_pin, 0, sizeof(bCommands_pin)); for (i = 7; i >= 0; i--) { /* for each AC Setcos operation */ bCommands_always <<= 1; bCommands_key <<= 1; if (p_idx[i] == -1) /* -1 means that bit is RFU -> set to 0 */ continue; bMethod = acl_to_byte_44(file->acl[ p_idx[i] ], &bNumber); /* Convert to OpenSc-index, convert to pin/key number */ switch(bMethod){ case SC_AC_NONE: /* always allowed */ bCommands_always |= 1; break; case SC_AC_CHV: /* pin */ if ((bNumber & 0x7F) == 0 || (bNumber & 0x7F) > 7) { 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)/sizeof(pins[0]), (int) bNumber)] |= 1 << i; break; case SC_AC_TERM: /* key */ bKeyNumber = bNumber; /* There should be only 1 key */ bCommands_key |= 1; break; } } /* Add the commands that are always allowed */ if (bCommands_always) { bBuf[len++] = 1; bBuf[len++] = bCommands_always; } /* Add commands that require pins */ for (i = 0; i < (int)sizeof(bCommands_pin) && pins[i] != -1; i++) { bBuf[len++] = 2; bBuf[len++] = bCommands_pin[i]; if (SETCOS_IS_EID_APPLET(card)) bBuf[len++] = pins[i]; /* pin ref */ else bBuf[len++] = pins[i] & 0x07; /* pin ref */ } /* Add commands that require the key */ if (bCommands_key) { bBuf[len++] = 2 | 0x20; /* indicate keyNumber present */ bBuf[len++] = bCommands_key; bBuf[len++] = bKeyNumber; } /* RSA signing/decryption requires AC adaptive coding, can't be put in AC simple coding. Only implemented for pins, not for a key. */ if ( (file->type == SC_FILE_TYPE_INTERNAL_EF) && (acl_to_byte_44(file->acl[SC_AC_OP_CRYPTO], &bNumber) == SC_AC_CHV) ) { bBuf[len++] = 0x83; bBuf[len++] = 0x01; bBuf[len++] = 0x2A; /* INS byte for the sign/decrypt APDU */ bBuf[len++] = bNumber & 0x07; /* pin ref */ } sc_file_set_sec_attr(file, bBuf, len); } return iso_ops->create_file(card, file); } static int setcos_create_file(sc_card_t *card, sc_file_t *file) { if (card->type == SC_CARD_TYPE_SETCOS_44 || SETCOS_IS_EID_APPLET(card)) return setcos_create_file_44(card, file); if (file->prop_attr_len == 0) sc_file_set_prop_attr(file, (const u8 *) "\x03\x00\x00", 3); if (file->sec_attr_len == 0) { int idx[6], i; u8 buf[6]; if (file->type == SC_FILE_TYPE_DF) { const int df_idx[6] = { SC_AC_OP_SELECT, SC_AC_OP_LOCK, SC_AC_OP_DELETE, SC_AC_OP_CREATE, SC_AC_OP_REHABILITATE, SC_AC_OP_INVALIDATE }; for (i = 0; i < 6; i++) idx[i] = df_idx[i]; } else { const int ef_idx[6] = { SC_AC_OP_READ, SC_AC_OP_UPDATE, SC_AC_OP_WRITE, SC_AC_OP_ERASE, SC_AC_OP_REHABILITATE, SC_AC_OP_INVALIDATE }; for (i = 0; i < 6; i++) idx[i] = ef_idx[i]; } for (i = 0; i < 6; i++) { const struct sc_acl_entry *entry; entry = sc_file_get_acl_entry(file, idx[i]); buf[i] = acl_to_byte(entry); } sc_file_set_sec_attr(file, buf, 6); } return iso_ops->create_file(card, file); } static int setcos_set_security_env2(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, locked = 0; assert(card != NULL && env != NULL); 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_SYMMETRIC) { sc_log(card->ctx, "symmetric keyref not supported.\n"); return SC_ERROR_NOT_SUPPORTED; } if (se_num > 0) { sc_log(card->ctx, "restore security environment not supported.\n"); return SC_ERROR_NOT_SUPPORTED; } } sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0, 0); switch (env->operation) { case SC_SEC_OPERATION_DECIPHER: /* Should be 0x81 */ apdu.p1 = 0x41; apdu.p2 = 0xB8; 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.p2 = 0xB6; break; default: return SC_ERROR_INVALID_ARGUMENTS; } apdu.le = 0; p = sbuf; if (env->flags & SC_SEC_ENV_ALG_REF_PRESENT) { *p++ = 0x80; /* algorithm reference */ *p++ = 0x01; *p++ = env->algorithm_ref & 0xFF; } if (env->flags & SC_SEC_ENV_FILE_REF_PRESENT) { *p++ = 0x81; *p++ = 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 && !(card->type == SC_CARD_TYPE_SETCOS_NIDEL || card->type == SC_CARD_TYPE_SETCOS_FINEID_V2_2048)) { if (env->flags & SC_SEC_ENV_KEY_REF_SYMMETRIC) *p++ = 0x83; else *p++ = 0x84; *p++ = env->key_ref_len; memcpy(p, env->key_ref, env->key_ref_len); p += env->key_ref_len; } r = (int)(p - sbuf); apdu.lc = r; apdu.datalen = r; apdu.data = sbuf; apdu.resplen = 0; if (se_num > 0) { r = sc_lock(card); LOG_TEST_RET(card->ctx, r, "sc_lock() failed"); locked = 1; } if (apdu.datalen != 0) { 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) return 0; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0xF2, se_num); r = sc_transmit_apdu(card, &apdu); sc_unlock(card); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); err: if (locked) sc_unlock(card); return r; } static int setcos_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_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_log(card->ctx, "Card does not support RSA.\n"); return SC_ERROR_NOT_SUPPORTED; break; } 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 setcos_set_security_env2(card, &tmp, se_num); } return setcos_set_security_env2(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 >> 4) { case 0: method = SC_AC_NONE; break; case 1: method = SC_AC_CHV; key_ref = 1; break; case 2: method = SC_AC_CHV; key_ref = 2; break; case 4: method = SC_AC_TERM; break; case 15: method = SC_AC_NEVER; break; default: method = SC_AC_UNKNOWN; 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; int idx[6]; if (len < 6) return; if (file->type == SC_FILE_TYPE_DF) { const int df_idx[6] = { SC_AC_OP_SELECT, SC_AC_OP_LOCK, SC_AC_OP_DELETE, SC_AC_OP_CREATE, SC_AC_OP_REHABILITATE, SC_AC_OP_INVALIDATE }; for (i = 0; i < 6; i++) idx[i] = df_idx[i]; } else { const int ef_idx[6] = { SC_AC_OP_READ, SC_AC_OP_UPDATE, SC_AC_OP_WRITE, SC_AC_OP_ERASE, SC_AC_OP_REHABILITATE, SC_AC_OP_INVALIDATE }; for (i = 0; i < 6; i++) idx[i] = ef_idx[i]; } for (i = 0; i < 6; i++) add_acl_entry(file, idx[i], buf[i]); } static void parse_sec_attr_44(sc_file_t *file, const u8 *buf, size_t len) { /* OpenSc Operation values for each command operation-type */ const int df_idx[8] = { /* byte 1 = OpenSC type of AC Bit0, byte 2 = OpenSC type of AC Bit1 ...*/ SC_AC_OP_DELETE, SC_AC_OP_CREATE, SC_AC_OP_CREATE, SC_AC_OP_INVALIDATE, SC_AC_OP_REHABILITATE, SC_AC_OP_LOCK, SC_AC_OP_DELETE, -1}; const int ef_idx[8] = { SC_AC_OP_READ, SC_AC_OP_UPDATE, SC_AC_OP_WRITE, SC_AC_OP_INVALIDATE, SC_AC_OP_REHABILITATE, -1, SC_AC_OP_ERASE, -1}; const int efi_idx[8] = { /* internal EF used for RSA keys */ SC_AC_OP_READ, SC_AC_OP_ERASE, SC_AC_OP_UPDATE, SC_AC_OP_INVALIDATE, SC_AC_OP_REHABILITATE, -1, SC_AC_OP_ERASE, -1}; u8 bValue; int i; int iKeyRef = 0; int iMethod; int iPinCount; int iOffset = 0; int iOperation; const int* p_idx; /* 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; 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 */ 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++; if (buf[iOffset+1] & 0x20) iParmLen++; if (buf[iOffset+1] & 0x10) iParmLen++; if (buf[iOffset+1] & 0x08) iParmLen++; /* Get KeyNumber if available */ if(iKeyLen) { int iSC; if (len < 1 + iACLen) break; iSC = buf[iOffset+iACLen]; switch( (iSC>>5) & 0x03 ){ case 0: iMethod = SC_AC_TERM; /* key authentication */ break; case 1: iMethod = SC_AC_AUT; /* key authentication */ break; case 2: case 3: iMethod = SC_AC_PRO; /* secure messaging */ break; } iKeyRef = iSC & 0x1F; /* get key number */ } /* 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; break; case 0x46: /* key-generation operation */ iOperation = SC_AC_OP_UPDATE; break; default: iOperation = SC_AC_OP_SELECT; break; } sc_file_add_acl_entry(file, iOperation, iMethod, iKeyRef); } else { /* AC in simple coding */ /* Initial AC is treated as an operational AC */ /* Get specific Cmd groups for specified file-type */ switch (file->type) { case SC_FILE_TYPE_DF: /* DF */ p_idx = df_idx; break; case SC_FILE_TYPE_INTERNAL_EF: /* EF for RSA keys */ p_idx = efi_idx; break; default: /* EF */ p_idx = ef_idx; break; } /* Encryption key present ? */ iPinCount = iACLen > 0 ? (int)iACLen - 1 : 0; if (buf[iOffset] & 0x20) { int iSC; if (len < 1 + (size_t)iACLen) break; iSC = buf[iOffset + iACLen]; switch( (iSC>>5) & 0x03 ) { case 0: iMethod = SC_AC_TERM; /* key authentication */ break; case 1: iMethod = SC_AC_AUT; /* key authentication */ break; case 2: case 3: iMethod = SC_AC_PRO; /* secure messaging */ break; } iKeyRef = iSC & 0x1F; /* get key number */ iPinCount--; /* one byte used for keyReference */ } /* Pin present ? */ if ( iPinCount > 0 ) { if (len < 1 + 2) break; iKeyRef = buf[iOffset + 2]; /* pin ref */ iMethod = SC_AC_CHV; } /* Add AC for each command-operationType into OpenSc structure */ bValue = buf[iOffset + 1]; for (i = 0; i < 8; i++) { if((bValue & 1) && (p_idx[i] >= 0)) sc_file_add_acl_entry(file, p_idx[i], iMethod, iKeyRef); bValue >>= 1; } } /* Current field treated, get next AC sub-field */ iOffset += iACLen +1; /* AC + PTL-byte */ len -= iACLen +1; } } static int setcos_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); /* 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) { if (card->type == SC_CARD_TYPE_SETCOS_44 || card->type == SC_CARD_TYPE_SETCOS_NIDEL || SETCOS_IS_EID_APPLET(card)) parse_sec_attr_44(*file, (*file)->sec_attr, (*file)->sec_attr_len); else parse_sec_attr(*file, (*file)->sec_attr, (*file)->sec_attr_len); } return 0; } static int setcos_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, 0xAA, 0, 0); if (card->type == SC_CARD_TYPE_SETCOS_44 || card->type == SC_CARD_TYPE_SETCOS_NIDEL || SETCOS_IS_EID_APPLET(card)) apdu.cla = 0x80; apdu.resp = buf; apdu.resplen = buflen; apdu.le = buflen > 256 ? 256 : buflen; r = sc_transmit_apdu(card, &apdu); 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 (int)apdu.resplen; } static int setcos_process_fci(sc_card_t *card, sc_file_t *file, const u8 *buf, size_t buflen) { int r = iso_ops->process_fci(card, file, buf, buflen); /* SetCOS 4.4: RSA key file is an internal EF but it's * file descriptor doesn't seem to follow ISO7816. */ if (r >= 0 && (card->type == SC_CARD_TYPE_SETCOS_44 || SETCOS_IS_EID_APPLET(card))) { const u8 *tag; size_t taglen = 1; tag = (u8 *) sc_asn1_find_tag(card->ctx, buf, buflen, 0x82, &taglen); if (tag != NULL && taglen == 1 && *tag == 0x11) file->type = SC_FILE_TYPE_INTERNAL_EF; } return r; } /* Write internal data, e.g. add default pin-records to pin-file */ static int setcos_putdata(struct sc_card *card, struct sc_cardctl_setcos_data_obj* data_obj) { int r; struct sc_apdu apdu; 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 = data_obj->P1; apdu.p2 = data_obj->P2; apdu.lc = data_obj->DataLen; apdu.datalen = data_obj->DataLen; apdu.data = data_obj->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, "PUT_DATA returned error"); LOG_FUNC_RETURN(card->ctx, r); } /* Read internal data, e.g. get RSA public key */ static int setcos_getdata(struct sc_card *card, struct sc_cardctl_setcos_data_obj* data_obj) { int r; struct sc_apdu apdu; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); memset(&apdu, 0, sizeof(apdu)); apdu.cse = SC_APDU_CASE_2_SHORT; apdu.cla = 0x00; apdu.ins = 0xCA; /* GET DATA */ apdu.p1 = data_obj->P1; apdu.p2 = data_obj->P2; apdu.lc = 0; apdu.datalen = 0; apdu.data = data_obj->Data; apdu.le = 256; apdu.resp = data_obj->Data; apdu.resplen = data_obj->DataLen; 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_DATA returned error"); if (apdu.resplen > data_obj->DataLen) r = SC_ERROR_WRONG_LENGTH; else data_obj->DataLen = apdu.resplen; LOG_FUNC_RETURN(card->ctx, r); } /* Generate or store a key */ static int setcos_generate_store_key(sc_card_t *card, struct sc_cardctl_setcos_gen_store_key_info *data) { struct sc_apdu apdu; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; int r, len; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); /* 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++] = 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; 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; 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; } sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x46, 0x00, 0x00); apdu.cla = 0x00; apdu.data = sbuf; apdu.datalen = len; apdu.lc = 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); LOG_TEST_RET(card->ctx, r, "STORE/GENERATE_KEY returned error"); LOG_FUNC_RETURN(card->ctx, r); } static int setcos_activate_file(sc_card_t *card) { int r; u8 sbuf[2]; sc_apdu_t apdu; sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x44, 0x00, 0x00); apdu.data = sbuf; 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, "ACTIVATE_FILE returned error"); LOG_FUNC_RETURN(card->ctx, r); } static int setcos_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) { if (card->type != SC_CARD_TYPE_SETCOS_44 && !SETCOS_IS_EID_APPLET(card)) return SC_ERROR_NOT_SUPPORTED; switch(cmd) { case SC_CARDCTL_SETCOS_PUTDATA: return setcos_putdata(card, (struct sc_cardctl_setcos_data_obj*) ptr); break; case SC_CARDCTL_SETCOS_GETDATA: return setcos_getdata(card, (struct sc_cardctl_setcos_data_obj*) ptr); break; case SC_CARDCTL_SETCOS_GENERATE_STORE_KEY: return setcos_generate_store_key(card, (struct sc_cardctl_setcos_gen_store_key_info *) ptr); case SC_CARDCTL_SETCOS_ACTIVATE_FILE: return setcos_activate_file(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(); setcos_ops = *iso_drv->ops; setcos_ops.match_card = setcos_match_card; setcos_ops.init = setcos_init; if (iso_ops == NULL) iso_ops = iso_drv->ops; setcos_ops.create_file = setcos_create_file; setcos_ops.set_security_env = setcos_set_security_env; setcos_ops.select_file = setcos_select_file; setcos_ops.list_files = setcos_list_files; setcos_ops.process_fci = setcos_process_fci; setcos_ops.construct_fci = setcos_construct_fci; setcos_ops.card_ctl = setcos_card_ctl; return &setcos_drv; } struct sc_card_driver *sc_get_setcos_driver(void) { return sc_get_driver(); } OpenSC-0.26.1/src/libopensc/card-skeid.c000066400000000000000000000124321474147347300176660ustar00rootroot00000000000000/* * 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; assert(card != NULL && env != NULL); 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; } OpenSC-0.26.1/src/libopensc/card-starcos.c000066400000000000000000001767041474147347300202620ustar00rootroot00000000000000/* * card-starcos.c: Support for STARCOS SPK 2.3 cards * * Copyright (C) 2003 Jörn Zukowski and * Nils Larsch , TrustCenter AG * * 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 "asn1.h" #include "cardctl.h" #include "internal.h" #include "iso7816.h" // 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 }, { "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", "starcos", &starcos_ops, NULL, 0, NULL }; 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"}, { 0x69F0, SC_ERROR_NOT_ALLOWED, "Command not allowed"}, { 0x6A89, SC_ERROR_FILE_ALREADY_EXISTS, "Files exists"}, { 0x6A8A, SC_ERROR_FILE_ALREADY_EXISTS, "Application exists"}, { 0x6F01, SC_ERROR_CARD_CMD_FAILED, "public key not complete"}, { 0x6F02, SC_ERROR_CARD_CMD_FAILED, "data overflow"}, { 0x6F03, SC_ERROR_CARD_CMD_FAILED, "invalid command sequence"}, { 0x6F05, SC_ERROR_CARD_CMD_FAILED, "security environment invalid"}, { 0x6F07, SC_ERROR_FILE_NOT_FOUND, "key part not found"}, { 0x6F08, SC_ERROR_CARD_CMD_FAILED, "signature failed"}, { 0x6F0A, SC_ERROR_INCORRECT_PARAMETERS, "key format does not match key length"}, { 0x6F0B, SC_ERROR_INCORRECT_PARAMETERS, "length of key component inconsistent with algorithm"}, { 0x6F81, SC_ERROR_CARD_CMD_FAILED, "system error"} }; /* internal structure to save the current security environment */ typedef struct starcos_ex_data_st { int sec_ops; /* the currently selected security operation, * i.e. SC_SEC_OPERATION_AUTHENTICATE etc. */ 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_log((card)->ctx, \ "not 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) { int i; i = _sc_match_atr(card, starcos_atrs, &card->type); if (i < 0) return 0; 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; starcos_ex_data *ex_data; ex_data = calloc(1, sizeof(starcos_ex_data)); if (ex_data == NULL) return SC_ERROR_OUT_OF_MEMORY; 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 | SC_ALGORITHM_ONBOARD_KEY_GEN | SC_ALGORITHM_RSA_PAD_ISO9796 | 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; card->caps = SC_CARD_CAP_RNG; if ( IS_V3x(card) ) { 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_PAD_PSS; _sc_card_add_rsa_alg(card, 512, flags, 0x10001); _sc_card_add_rsa_alg(card, 768, flags, 0x10001); _sc_card_add_rsa_alg(card,1024, flags, 0x10001); _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); _sc_card_add_rsa_alg(card,1024, flags, 0x10001); /* we need read_binary&friends with max 128 bytes per read */ card->max_send_size = 128; card->max_recv_size = 128; } if (sc_parse_ef_atr(card) == SC_SUCCESS) { 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) { max_recv_size = card->ef_atr->max_response_apdu; } if (card->ef_atr->max_command_apdu > 0) { 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; } static int starcos_finish(sc_card_t *card) { if (card->drv_data) free((starcos_ex_data *)card->drv_data); return 0; } static int process_fci(sc_context_t *ctx, sc_file_t *file, const u8 *buf, size_t buflen) { /* NOTE: According to the Starcos S 2.1 manual it's possible * that a SELECT DF returns as a FCI arbitrary data which * is stored in a object file (in the corresponding DF) * with the tag 0x6f. */ size_t taglen, len = buflen; const u8 *tag = NULL, *p; sc_log(ctx, "processing FCI bytes\n"); if (buflen < 2) return SC_ERROR_INTERNAL; if (buf[0] != 0x6f) return SC_ERROR_INVALID_DATA; len = (size_t)buf[1]; if (buflen - 2 < len) return SC_ERROR_INVALID_DATA; p = buf + 2; /* defaults */ file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = SC_FILE_EF_UNKNOWN; 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_log(ctx, " bytes in file: %d\n", bytes); file->size = bytes; } tag = sc_asn1_find_tag(ctx, p, len, 0x82, &taglen); if (tag != NULL) { const char *type = "unknown"; const char *structure = "unknown"; if (taglen == 1 && tag[0] == 0x01) { /* transparent EF */ type = "working EF"; structure = "transparent"; file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = SC_FILE_EF_TRANSPARENT; } else if (taglen == 1 && tag[0] == 0x11) { /* object EF */ type = "working EF"; structure = "object"; file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = SC_FILE_EF_TRANSPARENT; /* TODO */ } else if (taglen == 3 && tag[1] == 0x21) { type = "working EF"; file->record_length = tag[2]; file->type = SC_FILE_TYPE_WORKING_EF; /* linear fixed, cyclic or compute */ switch ( tag[0] ) { case 0x02: structure = "linear fixed"; file->ef_structure = SC_FILE_EF_LINEAR_FIXED; break; case 0x07: structure = "cyclic"; file->ef_structure = SC_FILE_EF_CYCLIC; break; case 0x17: structure = "compute"; file->ef_structure = SC_FILE_EF_UNKNOWN; break; default: structure = "unknown"; file->ef_structure = SC_FILE_EF_UNKNOWN; file->record_length = 0; break; } } sc_log(ctx, " type: %s\n", type); sc_log(ctx, " EF structure: %s\n", structure); } file->magic = SC_FILE_MAGIC; return SC_SUCCESS; } static int process_fci_v3_4(sc_context_t *ctx, sc_file_t *file, const u8 *buf, size_t buflen) { size_t taglen, len = buflen; const u8 *tag = NULL, *p; sc_log(ctx, "processing %"SC_FORMAT_LEN_SIZE_T"u FCI bytes\n", buflen); if (buflen < 2) return SC_ERROR_INTERNAL; if (buf[0] != 0x6f) return SC_ERROR_INVALID_DATA; len = (size_t)buf[1]; if (buflen - 2 < len) return SC_ERROR_INVALID_DATA; /* defaults */ file->type = SC_FILE_TYPE_WORKING_EF; if (len == 0) { SC_FUNC_RETURN(ctx, 2, SC_SUCCESS); } p = buf + 2; file->ef_structure = SC_FILE_TYPE_DF; file->shareable = 1; 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; sc_log(ctx, "filename %s", sc_dump_hex(file->name, file->namelen)); } return SC_SUCCESS; } static int process_fcp_v3_4(sc_context_t *ctx, sc_file_t *file, const u8 *buf, size_t buflen) { size_t taglen, len = buflen; const u8 *tag = NULL, *p; sc_log(ctx, "processing %"SC_FORMAT_LEN_SIZE_T"u FCP bytes\n", buflen); if (buflen < 2) return SC_ERROR_INTERNAL; if (buf[0] != 0x62) return SC_ERROR_INVALID_DATA; len = (size_t)buf[1]; if (buflen - 2 < len) return SC_ERROR_INVALID_DATA; p = buf + 2; tag = sc_asn1_find_tag(ctx, p, len, 0x80, &taglen); if (tag != NULL && taglen >= 2) { int bytes = (tag[0] << 8) + tag[1]; sc_log(ctx, " bytes in file: %d\n", bytes); file->size = bytes; } tag = sc_asn1_find_tag(ctx, p, len, 0xc5, &taglen); if (tag != NULL && taglen >= 2) { int bytes = (tag[0] << 8) + tag[1]; sc_log(ctx, " bytes in file 2: %d\n", bytes); file->size = bytes; } tag = sc_asn1_find_tag(ctx, p, len, 0x82, &taglen); if (tag != NULL) { const char *type = "unknown"; const char *structure = "unknown"; if (taglen >= 1) { unsigned char byte = tag[0]; if (byte & 0x40) { file->shareable = 1; } if (byte == 0x38) { type = "DF"; file->type = SC_FILE_TYPE_DF; file->shareable = 1; } switch (byte & 7) { case 1: /* transparent EF */ type = "working EF"; structure = "transparent"; file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = SC_FILE_EF_TRANSPARENT; break; case 2: /* linear fixed EF */ type = "working EF"; structure = "linear fixed"; file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = SC_FILE_EF_LINEAR_FIXED; break; case 4: /* linear variable EF */ type = "working EF"; structure = "linear variable"; file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = SC_FILE_EF_LINEAR_VARIABLE; break; case 6: /* cyclic EF */ type = "working EF"; structure = "cyclic"; file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = SC_FILE_EF_CYCLIC; break; default: /* use defaults from above */ break; } } sc_log(ctx, " type: %s\n", type); sc_log(ctx, " EF structure: %s\n", structure); if (taglen >= 2) { if (tag[1] != 0x41 || taglen != 5) { SC_FUNC_RETURN(ctx, 2,SC_ERROR_INVALID_DATA); } /* formatted EF */ file->record_length = (tag[2] << 8) + tag[3]; file->record_count = tag[4]; 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); } } 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\n", tag[0], tag[1]); } 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; sc_log(ctx, " filename %s", sc_dump_hex(file->name, file->namelen)); } tag = sc_asn1_find_tag(ctx, p, len, 0x8a, &taglen); if (tag != NULL && taglen == 1) { char* status = "unknown"; switch (tag[0]) { case 1: status = "creation"; file->status = SC_FILE_STATUS_CREATION; break; case 5: status = "operational active"; file->status = SC_FILE_STATUS_ACTIVATED; break; case 12: case 13: status = "creation"; file->status = SC_FILE_STATUS_INVALIDATED; break; default: break; } sc_log(ctx, " file status: %s\n", status); } file->magic = SC_FILE_MAGIC; return SC_SUCCESS; } static int starcos_select_aid(sc_card_t *card, const u8 aid[16], size_t len, sc_file_t **file_out) { sc_apdu_t apdu; int r; size_t i = 0; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 0x04, 0x0C); apdu.lc = len; apdu.data = (u8*)aid; apdu.datalen = len; apdu.resplen = 0; apdu.le = 0; r = sc_transmit_apdu(card, &apdu); 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); if (file_out) { sc_file_t *file = sc_file_new(); if (!file) 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++) 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); } static int starcos_select_fid(sc_card_t *card, unsigned int id_hi, unsigned int id_lo, sc_file_t **file_out, int is_file) { sc_apdu_t apdu; u8 data[] = {id_hi & 0xff, id_lo & 0xff}; u8 resp[SC_MAX_APDU_BUFFER_SIZE]; int bIsDF = 0, r; int isFCP = 0; int isMF = 0; /* request FCI to distinguish between EFs and DFs */ sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0x00, 0x00); apdu.p2 = 0x00; apdu.resp = (u8*)resp; apdu.resplen = SC_MAX_APDU_BUFFER_SIZE; apdu.le = 256; apdu.lc = 2; apdu.data = (u8*)data; apdu.datalen = 2; if ( IS_V3x(card) ) { if (id_hi == 0x3f && id_lo == 0x0) { apdu.p1 = 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) apdu.p1 = 0x2; apdu.p2 = 0x4; } else { // path component apdu.p1 = 0x1; apdu.p2 = 0x0; } } r = sc_transmit_apdu(card, &apdu); 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()) */ bIsDF = 1; apdu.p2 = 0x0C; apdu.cse = SC_APDU_CASE_3_SHORT; apdu.resplen = 0; apdu.le = 0; r = sc_transmit_apdu(card, &apdu); 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; apdu.p2 = 0x0; apdu.resplen = sizeof(resp); apdu.le = 256; apdu.lc = 2; r = sc_transmit_apdu(card, &apdu); 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 */ sc_apdu_t apdu2; u8 resp2[2]; sc_format_apdu(card, &apdu2, SC_APDU_CASE_2_SHORT, 0xB0, 0, 0); apdu2.resp = (u8*)resp2; apdu2.resplen = 2; apdu2.le = 1; apdu2.lc = 0; r = sc_transmit_apdu(card, &apdu2); 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; } else { isFCP = 1; } } 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) LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); file->id = (id_hi << 8) + id_lo; file->path = card->cache.current_path; if (bIsDF || isMF) { /* we have a DF */ 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; } else { /* ok, assume we have a EF */ if ( IS_V3x(card) ) { if (isFCP) { r = process_fcp_v3_4(card->ctx, file, apdu.resp, apdu.resplen); } else { r = process_fci_v3_4(card->ctx, file, apdu.resp, apdu.resplen); } } else { r = process_fci(card->ctx, file, apdu.resp, apdu.resplen); } if (r != SC_SUCCESS) { sc_file_free(file); return r; } *file_out = file; } } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); } static int starcos_select_file(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file_out) { u8 pathbuf[SC_MAX_PATH_SIZE], *path = pathbuf; 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_log(card->ctx, "current path (%s): %s (len: %"SC_FORMAT_LEN_SIZE_T"u)\n", card->cache.current_path.type == SC_PATH_TYPE_DF_NAME ? "aid" : "path", 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->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); 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 (pathtype == SC_PATH_TYPE_DF_NAME) { /* SELECT DF with AID */ /* Select with 1-16byte Application-ID */ r = starcos_select_aid(card, pathbuf, pathlen, file_out); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } else if (pathtype == SC_PATH_TYPE_PATH) { u8 n_pathbuf[SC_MAX_PATH_SIZE]; /* Select with path (sequence of File-IDs) */ /* Starcos (S 2.1 and SPK 2.3) only supports one * level of subdirectories, therefore a path is * at most 3 FID long (the last one being the FID * of a EF) => pathlen must be even and less than 6 */ 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); 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; memcpy(n_pathbuf+2, path, pathlen); path = n_pathbuf; pathlen += 2; } } for ( i=0; ictx, 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))) static u8 process_acl_entry(sc_file_t *in, unsigned int method, unsigned int in_def) { u8 def = (u8)in_def; const sc_acl_entry_t *entry = sc_file_get_acl_entry(in, method); if (!entry) return def; 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 if ((key_ref & 0x0f) == 1) /* SOPIN */ return (key_ref & 0x80 ? 0x10 : 0x00) | 0x01; else return (key_ref & 0x80 ? 0x10 : 0x00) | STARCOS_PINID2STATE(key_ref); } else if (entry->method & SC_AC_NEVER) return STARCOS_AC_NEVER; else return def; } /** starcos_process_acl * \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 occurred otherwise error code * * 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). */ static int starcos_process_acl(sc_card_t *card, sc_file_t *file, sc_starcos_create_data *data) { u8 tmp, *p; static const u8 def_key[] = {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08}; if (file->type == SC_FILE_TYPE_DF && file->id == 0x3f00) { p = data->data.mf.header; memcpy(p, def_key, 8); p += 8; *p++ = (file->size >> 8) & 0xff; *p++ = file->size & 0xff; /* guess isf size (mf_size / 4) */ *p++ = (file->size >> 10) & 0xff; *p++ = (file->size >> 2) & 0xff; /* ac create ef */ *p++ = process_acl_entry(file,SC_AC_OP_CREATE,STARCOS_AC_ALWAYS); /* ac create key */ *p++ = process_acl_entry(file,SC_AC_OP_CREATE,STARCOS_AC_ALWAYS); /* ac create df */ *p++ = process_acl_entry(file,SC_AC_OP_CREATE,STARCOS_AC_ALWAYS); /* use the same ac for register df and create df */ *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; /* combined mode */ else tmp = 0x00; /* no sm */ *p++ = tmp; /* use the same sm mode for all ops */ *p++ = tmp; *p = tmp; data->type = SC_STARCOS_MF_DATA; return SC_SUCCESS; } else if (file->type == SC_FILE_TYPE_DF){ p = data->data.df.header; *p++ = (file->id >> 8) & 0xff; *p++ = file->id & 0xff; if (file->namelen) { /* copy aid */ *p++ = file->namelen & 0xff; memset(p, 0, 16); memcpy(p, file->name, (u8)file->namelen); p += 16; } else { /* use the fid as aid */ *p++ = 2; memset(p, 0, 16); *p++ = (file->id >> 8) & 0xff; *p++ = file->id & 0xff; p += 14; } /* guess isf size */ *p++ = (file->size >> 10) & 0xff; /* ISF space */ *p++ = (file->size >> 2) & 0xff; /* ISF space */ /* ac create ef */ *p++ = process_acl_entry(file,SC_AC_OP_CREATE,STARCOS_AC_ALWAYS); /* ac create key */ *p++ = process_acl_entry(file,SC_AC_OP_CREATE,STARCOS_AC_ALWAYS); /* set sm byte (same for keys and ef) */ if (file->acl[SC_AC_OP_CREATE] && (sc_file_get_acl_entry(file, SC_AC_OP_CREATE)->method & SC_AC_PRO)) tmp = 0x03; else tmp = 0x00; *p++ = tmp; /* SM CR */ *p = tmp; /* SM ISF */ data->data.df.size[0] = (file->size >> 8) & 0xff; data->data.df.size[1] = file->size & 0xff; data->type = SC_STARCOS_DF_DATA; return SC_SUCCESS; } else if (file->type == SC_FILE_TYPE_WORKING_EF) { p = data->data.ef.header; *p++ = (file->id >> 8) & 0xff; *p++ = file->id & 0xff; /* ac read */ *p++ = process_acl_entry(file, SC_AC_OP_READ,STARCOS_AC_ALWAYS); /* ac write */ *p++ = process_acl_entry(file, SC_AC_OP_WRITE,STARCOS_AC_ALWAYS); /* ac erase */ *p++ = process_acl_entry(file, SC_AC_OP_ERASE,STARCOS_AC_ALWAYS); *p++ = STARCOS_AC_ALWAYS; /* AC LOCK */ *p++ = STARCOS_AC_ALWAYS; /* AC UNLOCK */ *p++ = STARCOS_AC_ALWAYS; /* AC INCREASE */ *p++ = STARCOS_AC_ALWAYS; /* AC DECREASE */ *p++ = 0x00; /* rfu */ *p++ = 0x00; /* rfu */ /* use sm (in combined mode) if wanted */ if ((file->acl[SC_AC_OP_READ] && (sc_file_get_acl_entry(file, SC_AC_OP_READ)->method & SC_AC_PRO)) || (file->acl[SC_AC_OP_UPDATE] && (sc_file_get_acl_entry(file, SC_AC_OP_UPDATE)->method & SC_AC_PRO)) || (file->acl[SC_AC_OP_WRITE] && (sc_file_get_acl_entry(file, SC_AC_OP_WRITE)->method & SC_AC_PRO)) ) tmp = 0x03; else tmp = 0x00; *p++ = tmp; /* SM byte */ *p++ = 0x00; /* use the least significant 5 bits * of the FID as SID */ switch (file->ef_structure) { case SC_FILE_EF_TRANSPARENT: *p++ = 0x81; *p++ = (file->size >> 8) & 0xff; *p = file->size & 0xff; break; case SC_FILE_EF_LINEAR_FIXED: *p++ = 0x82; *p++ = file->record_count & 0xff; *p = file->record_length & 0xff; break; case SC_FILE_EF_CYCLIC: *p++ = 0x84; *p++ = file->record_count & 0xff; *p = file->record_length & 0xff; break; default: return SC_ERROR_INVALID_ARGUMENTS; } data->type = SC_STARCOS_EF_DATA; return SC_SUCCESS; } else return SC_ERROR_INVALID_ARGUMENTS; } /** starcos_create_mf * internal function to create the MF * \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. */ static int starcos_create_mf(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_log(ctx, "creating MF \n"); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, 0x00, 0x00); apdu.cla |= 0x80; apdu.lc = 19; apdu.datalen = 19; apdu.data = (u8 *) data->data.mf.header; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(ctx, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); } /** starcos_create_df * internal function to create a DF * \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 functions registers and creates a DF based in the information * stored in a sc_starcos_create_data.df data structure. Note: CREATE END must * be called separately to activate the ACs. */ static int starcos_create_df(sc_card_t *card, sc_starcos_create_data *data) { int r; size_t len; sc_apdu_t apdu; sc_context_t *ctx = card->ctx; CHECK_NOT_SUPPORTED_V3_4(card); sc_log(ctx, "creating DF\n"); /* first step: REGISTER DF */ 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]); len = 3 + data->data.df.header[2]; apdu.cla |= 0x80; apdu.lc = len; apdu.datalen = len; apdu.data = data->data.df.header; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(ctx, r, "APDU transmit failed"); /* second step: CREATE DF */ sc_log(ctx, "calling CREATE DF\n"); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, 0x01, 0x00); apdu.cla |= 0x80; apdu.lc = 25; apdu.datalen = 25; apdu.data = data->data.df.header; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(ctx, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); } /** starcos_create_ef * internal function to create a EF * \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 a EF based on the information stored in * 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_log(ctx, "creating EF\n"); sc_format_apdu(card,&apdu,SC_APDU_CASE_3_SHORT,0xE0,0x03,0x00); apdu.cla |= 0x80; apdu.lc = 16; apdu.datalen = 16; apdu.data = (u8 *) data->data.ef.header; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); } /** starcos_create_end * internal function to activate the ACs * \param card pointer to the sc_card structure * \param file pointer to a sc_file object * \return SC_SUCCESS or error code * * 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) { int r; u8 fid[2]; sc_apdu_t apdu; if (file->type != SC_FILE_TYPE_DF) return SC_ERROR_INVALID_ARGUMENTS; CHECK_NOT_SUPPORTED_V3_4(card); fid[0] = (file->id >> 8) & 0xff; fid[1] = file->id & 0xff; sc_format_apdu(card,&apdu,SC_APDU_CASE_3_SHORT, 0xE0, 0x02, 0x00); apdu.cla |= 0x80; apdu.lc = 2; apdu.datalen = 2; apdu.data = fid; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); } /** starcos_create_file * \param card pointer to the sc_card structure * \param file pointer to a sc_file object * \return SC_SUCCESS or error code * * This function creates MF, DF or EF based on the supplied * 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; CHECK_NOT_SUPPORTED_V3_4(card); SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (file->type == SC_FILE_TYPE_DF) { if (file->id == 0x3f00) { /* CREATE MF */ r = starcos_process_acl(card, file, &data); if (r != SC_SUCCESS) return r; return starcos_create_mf(card, &data); } else { /* CREATE DF */ r = starcos_process_acl(card, file, &data); if (r != SC_SUCCESS) return r; return starcos_create_df(card, &data); } } else if (file->type == SC_FILE_TYPE_WORKING_EF) { /* CREATE EF */ r = starcos_process_acl(card, file, &data); if (r != SC_SUCCESS) return r; return starcos_create_ef(card, &data); } else return SC_ERROR_INVALID_ARGUMENTS; } /** starcos_erase_card * internal function to restore the delivery state * \param card pointer to the sc_card object * \return SC_SUCCESS or error code * * This function deletes the MF (for 'test cards' only). */ static int starcos_erase_card(sc_card_t *card) { /* restore the delivery state */ int r; u8 sbuf[2]; sc_apdu_t apdu = {0}; sbuf[0] = 0x3f; sbuf[1] = 0x00; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE4, 0x00, 0x00); apdu.cla |= 0x80; apdu.lc = 2; apdu.datalen = 2; apdu.data = sbuf; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); sc_invalidate_cache(card); if (apdu.sw1 == 0x69 && apdu.sw2 == 0x85) /* no MF to delete, ignore error */ return SC_SUCCESS; else return sc_check_sw(card, apdu.sw1, apdu.sw2); } #define STARCOS_WKEY_CSIZE 124 /** starcos_write_key * set key in isf * \param card pointer to the sc_card object * \param data pointer to a sc_starcos_wkey_data structure * \return SC_SUCCESS or error code * * This function installs a key header in the ISF (based on the * information supplied in the sc_starcos_wkey_data structure) * and set a supplied key (depending on the mode). */ static int starcos_write_key(sc_card_t *card, sc_starcos_wkey_data *data) { int r; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; const u8 *p; size_t len = sizeof(sbuf), tlen, offset = 0; sc_apdu_t apdu; CHECK_NOT_SUPPORTED_V3_4(card); if (data->mode == 0) { /* mode == 0 => install */ /* install key header */ sbuf[0] = 0xc1; /* key header tag */ sbuf[1] = 0x0c; /* key header length */ memcpy(sbuf + 2, data->key_header, 12); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xf4, data->mode, 0x00); apdu.cla |= 0x80; apdu.lc = 14; apdu.datalen = 14; 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) return sc_check_sw(card, apdu.sw1, apdu.sw2); if (data->key == NULL) return SC_SUCCESS; } if (data->key == NULL) return SC_ERROR_INVALID_ARGUMENTS; p = data->key; tlen = data->key_len; while (tlen != 0) { /* transmit the key in chunks of STARCOS_WKEY_CSIZE bytes */ u8 c_len = tlen < STARCOS_WKEY_CSIZE ? tlen : STARCOS_WKEY_CSIZE; sbuf[0] = 0xc2; sbuf[1] = 3 + c_len; sbuf[2] = data->kid; sbuf[3] = (offset >> 8) & 0xff; sbuf[4] = offset & 0xff; 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); 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 += c_len; p += c_len; tlen -= c_len; } return SC_SUCCESS; } /** starcos_gen_key * generate public key pair * \param card pointer to the sc_card object * \param data pointer to a sc_starcos_gen_key_data structure * \return SC_SUCCESS or error code * * This function generates a public key pair and stores the created * private key in the ISF (specified by the KID). */ static int starcos_gen_key(sc_card_t *card, sc_starcos_gen_key_data *data) { int r; size_t i, len = data->key_length >> 3; sc_apdu_t apdu; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; u8 sbuf[2], *p, *q; CHECK_NOT_SUPPORTED_V3_4(card); /* generate key */ 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); sbuf[1] = (u8)(data->key_length); apdu.data = sbuf; apdu.lc = 2; apdu.datalen = 2; r = sc_transmit_apdu(card, &apdu); 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 */ sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xf0, 0x9c, 0x00); sbuf[0] = data->key_id; apdu.cla |= 0x80; apdu.data = sbuf; apdu.datalen = 1; apdu.lc = 1; 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_check_sw(card, apdu.sw1, apdu.sw2); data->modulus = malloc(len); if (!data->modulus) return SC_ERROR_OUT_OF_MEMORY; p = data->modulus; /* XXX use tags to find starting position of the modulus */ q = &rbuf[18]; /* LSB to MSB -> MSB to LSB */ for (i = len; i != 0; i--) *p++ = q[i - 1]; return SC_SUCCESS; } /** starcos_set_security_env * sets the security environment * \param card pointer to the sc_card object * \param env pointer to a sc_security_env object * \param se_num not used here * \return SC_SUCCESS on success or an error code * * This function sets the security environment (using the starcos spk 2.3 * command MANAGE SECURITY ENVIRONMENT). In case a COMPUTE SIGNATURE * operation is requested , this function tries to detect whether * COMPUTE SIGNATURE or INTERNAL AUTHENTICATE must be used for signature * calculation. */ static int starcos_set_security_env(sc_card_t *card, const sc_security_env_t *env, int se_num) { u8 *p, *pp; int r, operation = env->operation; sc_apdu_t apdu; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; starcos_ex_data *ex_data = (starcos_ex_data *)card->drv_data; p = sbuf; 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); } /* Tag '84' (length 1) denotes key name or key reference */ *p++ = 0x84; *p++ = 0x01; 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; 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); 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 ((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 { ex_data->fix_digestInfo = env->algorithm_flags; } ex_data->sec_ops = SC_SEC_OPERATION_SIGN; return SC_SUCCESS; } /* copy key reference, if present */ if (env->flags & SC_SEC_ENV_KEY_REF_PRESENT) { if (env->flags & SC_SEC_ENV_KEY_REF_SYMMETRIC) *p++ = 0x83; else *p++ = 0x84; *p++ = env->key_ref_len; memcpy(p, env->key_ref, env->key_ref_len); p += env->key_ref_len; } pp = p; if (operation == SC_SEC_OPERATION_DECIPHER){ if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_02) { *p++ = 0x80; *p++ = 0x01; *p++ = 0x02; } else return SC_ERROR_INVALID_ARGUMENTS; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x81, 0xb8); apdu.data = sbuf; apdu.datalen = p - sbuf; apdu.lc = p - sbuf; apdu.le = 0; r = sc_transmit_apdu(card, &apdu); 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_TYPE_01 || env->algorithm_flags & SC_ALGORITHM_RSA_PAD_ISO9796)) { if (env->flags & SC_SEC_ENV_ALG_REF_PRESENT) { *p++ = 0x80; *p++ = 0x01; *p++ = env->algorithm_ref & 0xFF; } else if (env->flags & SC_SEC_ENV_ALG_PRESENT && env->algorithm == SC_ALGORITHM_RSA) { /* set the method to use based on the algorithm_flags */ *p++ = 0x80; *p++ = 0x01; 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) *p++ = 0x22; else if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_MD5) *p++ = 0x32; else { /* can't use COMPUTE SIGNATURE => * try INTERNAL AUTHENTICATE */ p = pp; operation = SC_SEC_OPERATION_AUTHENTICATE; goto try_authenticate; } } else if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_ISO9796) { if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA1) *p++ = 0x11; else if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_RIPEMD160) *p++ = 0x21; else return SC_ERROR_INVALID_ARGUMENTS; } else return SC_ERROR_INVALID_ARGUMENTS; } sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0xb6); apdu.data = sbuf; apdu.datalen = p - sbuf; apdu.lc = p - sbuf; apdu.le = 0; /* we don't know whether to use * COMPUTE SIGNATURE or INTERNAL AUTHENTICATE */ r = sc_transmit_apdu(card, &apdu); 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; return SC_SUCCESS; } /* reset pointer */ p = pp; /* doesn't work => try next op */ operation = SC_SEC_OPERATION_AUTHENTICATE; } try_authenticate: /* try INTERNAL 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); 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; ex_data->sec_ops = SC_SEC_OPERATION_AUTHENTICATE; return SC_SUCCESS; } return SC_ERROR_INVALID_ARGUMENTS; } static int starcos_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]; starcos_ex_data *ex_data = (starcos_ex_data *)card->drv_data; if (datalen > SC_MAX_APDU_BUFFER_SIZE) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); if (ex_data->sec_ops == SC_SEC_OPERATION_SIGN) { /* compute signature with the COMPUTE SIGNATURE command */ if ( IS_V3x(card) ) { size_t tmp_len; sc_format_apdu(card, &apdu, SC_APDU_CASE_4, 0x2A, 0x9E, 0x9A); 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; if (flags == 0x00) { flags = SC_ALGORITHM_RSA_HASH_NONE; } tmp_len = sizeof(sbuf); 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; } apdu.data = sbuf; apdu.datalen = tmp_len; apdu.lc = tmp_len; r = sc_transmit_apdu(card, &apdu); 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, 0x90, 0x81); apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = 0; memcpy(sbuf, data, datalen); apdu.data = sbuf; apdu.lc = datalen; apdu.datalen = datalen; r = sc_transmit_apdu(card, &apdu); 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)); /* call COMPUTE SIGNATURE */ sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x2A, 0x9E, 0x9A); 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) { size_t len = apdu.resplen > outlen ? outlen : apdu.resplen; 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; CHECK_NOT_SUPPORTED_V3_4(card); /* call INTERNAL AUTHENTICATE */ sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x88, 0x10, 0x00); /* fix/create DigestInfo structure (if necessary) */ if (ex_data->fix_digestInfo) { unsigned int flags = ex_data->fix_digestInfo & SC_ALGORITHM_RSA_HASHES; if (flags == 0x0) /* XXX: assume no hash is wanted */ flags = SC_ALGORITHM_RSA_HASH_NONE; tmp_len = sizeof(sbuf); r = sc_pkcs1_encode(card->ctx, flags, data, datalen, sbuf, &tmp_len, sizeof(sbuf)*8, NULL); if (r < 0) return r; } else { memcpy(sbuf, data, datalen); tmp_len = datalen; } apdu.lc = tmp_len; apdu.data = sbuf; apdu.datalen = tmp_len; 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) { size_t len = apdu.resplen > outlen ? outlen : apdu.resplen; memcpy(out, apdu.resp, 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); /* clear old state */ ex_data->sec_ops = 0; ex_data->fix_digestInfo = 0; 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_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_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_log(card->ctx, "%s\n", starcos_errors[i].errorstr); return starcos_errors[i].errorno; } /* iso error */ return iso_ops->check_sw(card, sw1, sw2); } static int starcos_get_serialnr(sc_card_t *card, sc_serial_number_t *serial) { int r; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; sc_apdu_t apdu; 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; } 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; } static int starcos_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) { sc_starcos_create_data *tmp; switch (cmd) { case SC_CARDCTL_STARCOS_CREATE_FILE: tmp = (sc_starcos_create_data *) ptr; if (tmp->type == SC_STARCOS_MF_DATA) return starcos_create_mf(card, tmp); else if (tmp->type == SC_STARCOS_DF_DATA) return starcos_create_df(card, tmp); else if (tmp->type == SC_STARCOS_EF_DATA) return starcos_create_ef(card, tmp); else return SC_ERROR_INTERNAL; case SC_CARDCTL_STARCOS_CREATE_END: return starcos_create_end(card, (sc_file_t *)ptr); case SC_CARDCTL_STARCOS_WRITE_KEY: return starcos_write_key(card, (sc_starcos_wkey_data *)ptr); case SC_CARDCTL_STARCOS_GENERATE_KEY: return starcos_gen_key(card, (sc_starcos_gen_key_data *)ptr); case SC_CARDCTL_ERASE_CARD: return starcos_erase_card(card); case SC_CARDCTL_GET_SERIALNR: return starcos_get_serialnr(card, (sc_serial_number_t *)ptr); default: return SC_ERROR_NOT_SUPPORTED; } } 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); 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, * that no MF exists, but then there's no need to logout * => return SC_SUCCESS */ return SC_SUCCESS; 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) { int r; 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, tries_left); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } 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; } struct sc_card_driver * sc_get_starcos_driver(void) { return sc_get_driver(); } OpenSC-0.26.1/src/libopensc/card-tcos.c000066400000000000000000000554361474147347300175520ustar00rootroot00000000000000/* * card-tcos.c: Support for TCOS cards * * Copyright (C) 2011 Peter Koch * Copyright (C) 2002 g10 Code GmbH * 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., 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" #include "asn1.h" #include "cardctl.h" 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 }, /* 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 } }; static struct sc_card_operations tcos_ops; static struct sc_card_driver tcos_drv = { "TCOS 3.0", "tcos", &tcos_ops, NULL, 0, NULL }; static const struct sc_card_operations *iso_ops = NULL; typedef struct tcos_data_st { unsigned long pad_flags; unsigned int next_sign; } tcos_data; static int tcos_finish(sc_card_t *card) { free(card->drv_data); return 0; } static int tcos_match_card(sc_card_t *card) { int i; i = _sc_match_atr(card, tcos_atrs, &card->type); if (i < 0) return 0; return 1; } static int tcos_init(sc_card_t *card) { unsigned long flags; tcos_data *data = malloc(sizeof(tcos_data)); if (!data) return SC_ERROR_OUT_OF_MEMORY; card->name = "TCOS"; 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; _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); } return 0; } /* Hmmm, I don't know what to do. It seems that the ACL design of OpenSC should be enhanced to allow for the command based security attributes of TCOS. FIXME: This just allows to create a very basic file. */ static int tcos_construct_fci(const sc_file_t *file, u8 *out, size_t *outlen) { u8 *p = out; u8 buf[64]; size_t n; /* FIXME: possible buffer overflow */ *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; buf[n] = file->shareable ? 0x40 : 0; switch (file->type) { case SC_FILE_TYPE_WORKING_EF: break; case SC_FILE_TYPE_DF: buf[0] |= 0x38; break; default: 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; } sc_asn1_put_tag(0x82, buf, n, p, 8, &p); /* 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, p, 16, &p); } 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); } } /* 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 */ 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; } 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; } static int tcos_create_file(sc_card_t *card, sc_file_t *file) { int r; size_t len; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; sc_apdu_t apdu; len = SC_MAX_APDU_BUFFER_SIZE; r = tcos_construct_fci(file, sbuf, &len); 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.lc = len; apdu.datalen = len; apdu.data = sbuf; r = sc_transmit_apdu(card, &apdu); 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) { unsigned int op = (unsigned int)-1; switch ( (commandbyte & 0xfe) ) { case 0xe2: /* append record */ op = SC_AC_OP_UPDATE; break; case 0x24: /* change password */ op = SC_AC_OP_UPDATE; break; case 0xe0: /* create */ op = SC_AC_OP_CREATE; break; case 0xe4: /* delete */ op = SC_AC_OP_DELETE; break; case 0xe8: /* exclude sfi */ op = SC_AC_OP_WRITE; break; case 0x82: /* external auth */ op = SC_AC_OP_READ; break; case 0xe6: /* include sfi */ op = SC_AC_OP_WRITE; break; case 0x88: /* internal auth */ op = SC_AC_OP_READ; break; case 0x04: /* invalidate */ op = SC_AC_OP_INVALIDATE; break; case 0x2a: /* perform sec. op */ op = SC_AC_OP_SELECT; break; case 0xb0: /* read binary */ op = SC_AC_OP_READ; break; case 0xb2: /* read record */ op = SC_AC_OP_READ; break; case 0x44: /* rehabilitate */ op = SC_AC_OP_REHABILITATE; break; case 0xa4: /* select */ op = SC_AC_OP_SELECT; break; case 0xee: /* set permanent */ op = SC_AC_OP_CREATE; break; case 0x2c: /* unblock password */op = SC_AC_OP_WRITE; break; case 0xd6: /* update binary */ op = SC_AC_OP_WRITE; break; case 0xdc: /* update record */ op = SC_AC_OP_WRITE; break; case 0x20: /* verify password */ op = SC_AC_OP_SELECT; break; case 0x60: /* admin group */ op = SC_AC_OP_CREATE; break; } return op; } /* Hmmm, I don't know what to do. It seems that the ACL design of OpenSC should be enhanced to allow for the command based security attributes of TCOS. FIXME: This just allows to create a very basic file. */ 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, 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, 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, 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, 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, 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, SC_AC_NONE, SC_AC_KEY_REF_NONE); 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_AC_NONE, SC_AC_KEY_REF_NONE); 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_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, SC_AC_CHV, buf[1]); 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, 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) { 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; int r; size_t pathlen; assert(card != NULL && in_path != NULL); 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; /* fall through */ case SC_PATH_TYPE_FROM_CURRENT: apdu.p1 = 9; break; case SC_PATH_TYPE_DF_NAME: apdu.p1 = 4; break; case SC_PATH_TYPE_PATH: apdu.p1 = 8; if (pathlen >= 2 && memcmp(path, "\x3F\x00", 2) == 0) path += 2, pathlen -= 2; if (pathlen == 0) apdu.p1 = 0; break; case SC_PATH_TYPE_PARENT: apdu.p1 = 3; pathlen = 0; break; default: SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); } if( pathlen == 0 ) apdu.cse = SC_APDU_CASE_2_SHORT; apdu.lc = pathlen; apdu.data = path; apdu.datalen = pathlen; if (file_out != NULL) { apdu.resp = buf; apdu.resplen = sizeof(buf); apdu.le = 256; } else { apdu.resplen = 0; 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); 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_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) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); *file_out = file; file->path = *in_path; iso_ops->process_fci(card, file, apdu.resp, apdu.resplen); parse_sec_attr(card, file, file->sec_attr, file->sec_attr_len); return 0; } static int tcos_list_files(sc_card_t *card, u8 *buf, size_t buflen) { sc_context_t *ctx; sc_apdu_t apdu; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE], p1; int r, count = 0; assert(card != NULL); ctx = card->ctx; for (p1=1; p1<=2; p1++) { sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xAA, p1, 0); apdu.cla = 0x80; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = 256; r = sc_transmit_apdu(card, &apdu); 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); LOG_TEST_RET(ctx, r, "List Dir failed"); if (apdu.resplen > buflen) return SC_ERROR_BUFFER_TOO_SMALL; 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; buflen -= apdu.resplen; count += apdu.resplen; } return count; } static int tcos_delete_file(sc_card_t *card, const sc_path_t *path) { int r; u8 sbuf[2]; 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_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.lc = 2; apdu.datalen = 2; apdu.data = sbuf; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); } static int tcos_set_security_env(sc_card_t *card, const sc_security_env_t *env, int se_num) { sc_context_t *ctx; sc_apdu_t apdu; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE], *p; int r, default_key, tcos3; tcos_data *data; assert(card != NULL && env != NULL); 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)) { LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); } if(!(env->flags & SC_SEC_ENV_KEY_REF_PRESENT)) sc_log(ctx, "No Key-Reference in SecEnvironment\n"); else 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_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; if (env->flags & SC_SEC_ENV_KEY_REF_PRESENT) { *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; } apdu.data = sbuf; apdu.lc = apdu.datalen = (p - sbuf); r=sc_transmit_apdu(card, &apdu); if (r) { sc_log(ctx, "%s: APDU transmit failed", sc_strerror(r)); return r; } if (apdu.sw1==0x6A && (apdu.sw2==0x81 || apdu.sw2==0x88)) { sc_log(ctx, "Detected Signature-Only key\n"); if (env->operation==SC_SEC_OPERATION_SIGN && default_key) return SC_SUCCESS; } SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); } static int tcos_restore_security_env(sc_card_t *card, int se_num) { return 0; } static int tcos_compute_signature(sc_card_t *card, const u8 * data, size_t datalen, u8 * out, size_t outlen) { 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; if (card == NULL || data == NULL || out == NULL) { return SC_ERROR_INVALID_ARGUMENTS; } tcos3 = (card->type == SC_CARD_TYPE_TCOS_V3); // 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; } else { size_t keylen = tcos3 ? 256 : 128; if (datalen > 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); apdu.le = tcos3 ? 256 : 128; apdu.data = sbuf; apdu.lc = apdu.datalen = dlen; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, 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); apdu.le = 128; apdu.data = sbuf; apdu.lc = apdu.datalen = dlen; 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); 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)); } static int tcos_decipher(sc_card_t *card, const u8 * crgram, size_t crgram_len, u8 * out, size_t outlen) { sc_context_t *ctx; sc_apdu_t apdu; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; tcos_data *data; int tcos3, r; 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; 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); 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_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); LOG_TEST_RET(ctx, 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_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; } if (offset < len) memcpy(out, apdu.resp + offset, len - offset); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, (int)(len - offset)); } SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); } /* Issue the SET PERMANENT command. With ENABLE_NULLPIN set the NullPIN method will be activated, otherwise the permanent operation will be done on the active file. */ static int tcos_setperm(sc_card_t *card, int enable_nullpin) { int r; sc_apdu_t apdu; 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.lc = 0; apdu.datalen = 0; apdu.data = NULL; r = sc_transmit_apdu(card, &apdu); 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; 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; } 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; } /* copy and return serial number */ memcpy(serial, &card->serialnr, sizeof(*serial)); return SC_SUCCESS; } static int tcos_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) { switch (cmd) { case SC_CARDCTL_TCOS_SETPERM: return tcos_setperm(card, !!ptr); case SC_CARDCTL_GET_SERIALNR: return tcos_get_serialnr(card, (sc_serial_number_t *)ptr); } return SC_ERROR_NOT_SUPPORTED; } struct sc_card_driver * sc_get_tcos_driver(void) { struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); if (iso_ops == NULL) iso_ops = iso_drv->ops; tcos_ops = *iso_drv->ops; tcos_ops.match_card = tcos_match_card; tcos_ops.init = tcos_init; tcos_ops.finish = tcos_finish; tcos_ops.create_file = tcos_create_file; tcos_ops.set_security_env = tcos_set_security_env; tcos_ops.select_file = tcos_select_file; tcos_ops.list_files = tcos_list_files; tcos_ops.delete_file = tcos_delete_file; 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; } OpenSC-0.26.1/src/libopensc/card.c000066400000000000000000001312231474147347300165710ustar00rootroot00000000000000/* * card.c: General smart card functions * * Copyright (C) 2001, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #ifdef HAVE_UNISTD_H #include #endif #include #include #include "reader-tr03119.h" #include "internal.h" #include "asn1.h" #include "common/compat_strlcpy.h" #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); static int sc_card_sm_check(sc_card_t *card); #endif int sc_check_sw(sc_card_t *card, unsigned int sw1, unsigned int sw2) { if (card == NULL) return SC_ERROR_INVALID_ARGUMENTS; if (card->ops->check_sw == NULL) return SC_ERROR_NOT_SUPPORTED; return card->ops->check_sw(card, sw1, sw2); } void sc_format_apdu(sc_card_t *card, sc_apdu_t *apdu, int cse, int ins, int p1, int p2) { if (card == NULL || apdu == NULL) { return; } memset(apdu, 0, sizeof(*apdu)); apdu->cla = (u8) card->cla; apdu->cse = cse; apdu->ins = (u8) ins; apdu->p1 = (u8) p1; 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; if (ctx == NULL) return NULL; card = calloc(1, sizeof(struct sc_card)); if (card == NULL) return NULL; card->ops = malloc(sizeof(struct sc_card_operations)); if (card->ops == NULL) { free(card); return NULL; } card->ctx = ctx; if (sc_mutex_create(ctx, &card->mutex) != SC_SUCCESS) { free(card->ops); free(card); return NULL; } card->type = -1; card->app_count = -1; return card; } static void sc_card_free(sc_card_t *card) { sc_free_apps(card); sc_free_ef_atr(card); 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); } } free(card->algorithms); card->algorithms = NULL; 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) sc_log(card->ctx, "unable to destroy mutex"); } sc_mem_clear(card, sizeof(*card)); free(card); } size_t sc_get_max_recv_size(const sc_card_t *card) { size_t max_recv_size; if (card == NULL || card->reader == NULL) { return 0; } max_recv_size = card->max_recv_size; /* initialize max_recv_size to a meaningful value */ if (card->caps & SC_CARD_CAP_APDU_EXT) { if (!max_recv_size) max_recv_size = 65536; } else { if (!max_recv_size) max_recv_size = 256; } /* Override card limitations with reader limitations. */ if (card->reader->max_recv_size != 0 && (card->reader->max_recv_size < card->max_recv_size)) max_recv_size = card->reader->max_recv_size; return max_recv_size; } size_t sc_get_max_send_size(const sc_card_t *card) { size_t max_send_size; if (card == NULL || card->reader == NULL) { return 0; } max_send_size = card->max_send_size; /* 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) max_send_size = 65535; } else { if (!max_send_size) max_send_size = 255; } /* Override card limitations with reader limitations. */ if (card->reader->max_send_size != 0 && (card->reader->max_send_size < card->max_send_size)) max_send_size = card->reader->max_send_size; return max_send_size; } int sc_connect_card(sc_reader_t *reader, sc_card_t **card_out) { sc_card_t *card; sc_context_t *ctx; struct sc_card_driver *driver; int i, r = 0, idx, connected = 0; if (card_out == NULL || reader == NULL) return SC_ERROR_INVALID_ARGUMENTS; ctx = reader->ctx; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); if (reader->ops->connect == NULL) LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); card = sc_card_new(ctx); if (card == NULL) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); r = reader->ops->connect(reader); if (r) goto err; connected = 1; card->reader = reader; card->ctx = ctx; if (reader->flags & SC_READER_ENABLE_ESCAPE) sc_detect_escape_cmds(reader); memcpy(&card->atr, &reader->atr, sizeof(card->atr)); memcpy(&card->uid, &reader->uid, sizeof(card->uid)); _sc_parse_atr(reader); /* See if the ATR matches any ATR specified in the config file */ if ((driver = ctx->forced_driver) == NULL) { sc_log(ctx, "matching configured ATRs"); for (i = 0; ctx->card_drivers[i] != NULL; i++) { driver = ctx->card_drivers[i]; if (driver->atr_map == NULL || !strcmp(driver->short_name, "default")) { driver = NULL; continue; } sc_log(ctx, "trying driver '%s'", driver->short_name); idx = _sc_match_atr(card, driver->atr_map, NULL); if (idx >= 0) { struct sc_atr_table *src = &driver->atr_map[idx]; sc_log(ctx, "matched driver '%s'", driver->name); /* It's up to card driver to notice these correctly */ card->name = src->name; card->type = src->type; card->flags = src->flags; break; } driver = NULL; } } if (driver != NULL) { /* Forced driver, or matched via ATR mapping from config file */ card->driver = driver; memcpy(card->ops, card->driver->ops, sizeof(struct sc_card_operations)); if (card->ops->match_card != NULL) if (card->ops->match_card(card) != 1) sc_log(ctx, "driver '%s' match_card() failed: %s (will continue anyway)", card->driver->name, sc_strerror(r)); if (card->ops->init != NULL) { r = card->ops->init(card); if (r) { sc_log(ctx, "driver '%s' init() failed: %s", card->driver->name, sc_strerror(r)); goto err; } } } 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; sc_log(ctx, "trying driver '%s'", drv->short_name); if (ops == NULL || ops->match_card == NULL) { continue; } else if (!(ctx->flags & SC_CTX_FLAG_ENABLE_DEFAULT_DRIVER) && !strcmp("default", drv->short_name)) { sc_log(ctx , "ignore 'default' card driver"); continue; } /* Needed if match_card() needs to talk with the card (e.g. card-muscle) */ *card->ops = *ops; if (ops->match_card(card) != 1) continue; sc_log(ctx, "matched: %s", drv->name); memcpy(card->ops, ops, sizeof(struct sc_card_operations)); card->driver = drv; r = ops->init(card); if (r) { sc_log(ctx, "driver '%s' init() failed: %s", drv->name, sc_strerror(r)); if (r == SC_ERROR_INVALID_CARD) { card->driver = NULL; continue; } goto err; } break; } } if (card->driver == NULL) { sc_log(ctx, "unable to find driver for inserted card"); r = SC_ERROR_INVALID_CARD; goto err; } if (card->name == NULL) card->name = card->driver->name; /* 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); sc_log(ctx, "card info name:'%s', type:%i, flags:0x%lX, max_send/recv_size:%"SC_FORMAT_LEN_SIZE_T"u/%"SC_FORMAT_LEN_SIZE_T"u", card->name, card->type, card->flags, card->max_send_size, card->max_recv_size); #ifdef ENABLE_SM /* Check, if secure messaging module present. */ r = sc_card_sm_check(card); if (r) { sc_log(ctx, "cannot load secure messaging module"); goto err; } #endif *card_out = card; LOG_FUNC_RETURN(ctx, SC_SUCCESS); err: if (connected) reader->ops->disconnect(reader); if (card != NULL) sc_card_free(card); LOG_FUNC_RETURN(ctx, r); } int sc_disconnect_card(sc_card_t *card) { sc_context_t *ctx; if (!card) return SC_ERROR_INVALID_ARGUMENTS; ctx = card->ctx; LOG_FUNC_CALLED(ctx); if (card->ops->finish) { int r = card->ops->finish(card); if (r) sc_log(ctx, "card driver finish() failed: %s", sc_strerror(r)); } if (card->reader->ops->disconnect) { int r = card->reader->ops->disconnect(card->reader); if (r) sc_log(ctx, "disconnect() failed: %s", sc_strerror(r)); } #ifdef ENABLE_SM /* release SM related resources */ sc_card_sm_unload(card); #endif sc_card_free(card); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } int sc_reset(sc_card_t *card, int do_cold_reset) { int r, r2; if (card == NULL) return SC_ERROR_INVALID_ARGUMENTS; if (card->reader->ops->reset == NULL) return SC_ERROR_NOT_SUPPORTED; r = sc_mutex_lock(card->ctx, card->mutex); if (r != SC_SUCCESS) return r; r = card->reader->ops->reset(card->reader, do_cold_reset); sc_invalidate_cache(card); r2 = sc_mutex_unlock(card->ctx, card->mutex); if (r2 != SC_SUCCESS) { sc_log(card->ctx, "unable to release lock"); r = r != SC_SUCCESS ? r : r2; } return r; } int sc_lock(sc_card_t *card) { int r = 0, r2 = 0; int was_reset = 0; int reader_lock_obtained = 0; if (card == NULL) return SC_ERROR_INVALID_ARGUMENTS; LOG_FUNC_CALLED(card->ctx); r = sc_mutex_lock(card->ctx, card->mutex); if (r != SC_SUCCESS) return r; if (card->lock_count == 0) { if (card->reader->ops->lock != NULL) { r = card->reader->ops->lock(card->reader); while (r == SC_ERROR_CARD_RESET || r == SC_ERROR_READER_REATTACHED) { sc_invalidate_cache(card); if (was_reset++ > 4) /* TODO retry a few times */ break; r = card->reader->ops->lock(card->reader); } 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) card->sm_ctx.ops.open(card); #endif } /* 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) { 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); } int sc_unlock(sc_card_t *card) { int r, r2; if (!card) return SC_ERROR_INVALID_ARGUMENTS; LOG_FUNC_CALLED(card->ctx); r = sc_mutex_lock(card->ctx, card->mutex); if (r != SC_SUCCESS) LOG_FUNC_RETURN(card->ctx, r); if (card->lock_count < 1) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } if (--card->lock_count == 0) { if (card->flags & SC_CARD_FLAG_KEEP_ALIVE) { /* Multiple processes accessing the card will most likely render * the card cache useless. To not have a bad cache, we explicitly * invalidate it. */ sc_invalidate_cache(card); } /* release reader lock */ if (card->reader->ops->unlock != NULL) r = card->reader->ops->unlock(card->reader); } r2 = sc_mutex_unlock(card->ctx, card->mutex); if (r2 != SC_SUCCESS) { sc_log(card->ctx, "unable to release lock"); r = (r == SC_SUCCESS) ? r2 : r; } return r; } int sc_list_files(sc_card_t *card, u8 *buf, size_t buflen) { int r; if (card == NULL) { return SC_ERROR_INVALID_ARGUMENTS; } LOG_FUNC_CALLED(card->ctx); if (card->ops->list_files == NULL) LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); r = card->ops->list_files(card, buf, buflen); LOG_FUNC_RETURN(card->ctx, r); } int sc_create_file(sc_card_t *card, sc_file_t *file) { int r; char pbuf[SC_MAX_PATH_STRING_SIZE]; const sc_path_t *in_path; if (card == NULL || file == NULL) { return SC_ERROR_INVALID_ARGUMENTS; } in_path = &file->path; r = sc_path_print(pbuf, sizeof(pbuf), in_path); if (r != SC_SUCCESS) pbuf[0] = '\0'; sc_log(card->ctx, "called; type=%d, path=%s, id=%04i, size=%"SC_FORMAT_LEN_SIZE_T"u", in_path->type, pbuf, file->id, file->size); /* ISO 7816-4: "Number of data bytes in the file, including structural information if any" * can not be bigger than two bytes */ if (file->size > 0xFFFF) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); if (card->ops->create_file == NULL) LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); r = card->ops->create_file(card, file); LOG_FUNC_RETURN(card->ctx, r); } int sc_delete_file(sc_card_t *card, const sc_path_t *path) { int r; char pbuf[SC_MAX_PATH_STRING_SIZE]; if (card == NULL) { return SC_ERROR_INVALID_ARGUMENTS; } r = sc_path_print(pbuf, sizeof(pbuf), path); if (r != SC_SUCCESS) pbuf[0] = '\0'; sc_log(card->ctx, "called; type=%d, path=%s", path->type, pbuf); if (card->ops->delete_file == NULL) LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); r = card->ops->delete_file(card, path); LOG_FUNC_RETURN(card->ctx, r); } int sc_read_binary(sc_card_t *card, unsigned int idx, 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) { return SC_ERROR_INVALID_ARGUMENTS; } 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, SC_SUCCESS); #ifdef ENABLE_SM if (card->sm_ctx.ops.read_binary) { r = card->sm_ctx.ops.read_binary(card, idx, buf, count); if (r) LOG_FUNC_RETURN(card->ctx, r); } #endif if (card->ops->read_binary == NULL) LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); /* 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); } 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_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) { return SC_ERROR_INVALID_ARGUMENTS; } 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, SC_SUCCESS); if (card->ops->write_binary == NULL) LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); /* 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); } 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_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) { return SC_ERROR_INVALID_ARGUMENTS; } 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, SC_SUCCESS); #ifdef ENABLE_SM if (card->sm_ctx.ops.update_binary) { r = card->sm_ctx.ops.update_binary(card, idx, buf, count); if (r) LOG_FUNC_RETURN(card->ctx, r); } #endif if (card->ops->update_binary == NULL) LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); /* 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); } 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_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, 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); /* 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)); } int sc_select_file(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file) { int r; char pbuf[SC_MAX_PATH_STRING_SIZE]; if (card == NULL || in_path == NULL) { return SC_ERROR_INVALID_ARGUMENTS; } r = sc_path_print(pbuf, sizeof(pbuf), in_path); if (r != SC_SUCCESS) pbuf[0] = '\0'; /* FIXME We should be a bit less strict and let the upper layers do * the initialization (including reuse of existing file objects). We * implemented this here because we are lazy. */ if (file != NULL) *file = NULL; sc_log(card->ctx, "called; type=%d, path=%s", in_path->type, pbuf); if (in_path->len > SC_MAX_PATH_SIZE) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); if (in_path->type == SC_PATH_TYPE_PATH) { /* Perform a sanity check */ size_t i; if ((in_path->len & 1) != 0) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); for (i = 0; i < in_path->len/2; i++) { u8 p1 = in_path->value[2*i], p2 = in_path->value[2*i+1]; if ((p1 == 0x3F && p2 == 0x00) && i != 0) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } } if (card->ops->select_file == NULL) LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); r = card->ops->select_file(card, in_path, file); LOG_TEST_RET(card->ctx, r, "'SELECT' error"); if (file) { if (*file) /* Remember file path */ (*file)->path = *in_path; else /* FIXME We should be a bit less strict and let the upper layers do * the error checking. We implemented this here because we are * lazy. */ r = SC_ERROR_INVALID_DATA; } LOG_FUNC_RETURN(card->ctx, r); } int sc_get_data(sc_card_t *card, unsigned int tag, u8 *buf, size_t len) { int r; sc_log(card->ctx, "called, tag=%04x", tag); if (card->ops->get_data == NULL) LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); r = card->ops->get_data(card, tag, buf, len); LOG_FUNC_RETURN(card->ctx, r); } int sc_put_data(sc_card_t *card, unsigned int tag, const u8 *buf, size_t len) { int r; sc_log(card->ctx,"called, tag=%04x", tag); if (card->ops->put_data == NULL) LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); r = card->ops->put_data(card, tag, buf, len); LOG_FUNC_RETURN(card->ctx, r); } int sc_get_challenge(sc_card_t *card, u8 *rnd, size_t len) { int r; if (len == 0) return SC_SUCCESS; if (card == NULL || rnd == NULL) { return SC_ERROR_INVALID_ARGUMENTS; } LOG_FUNC_CALLED(card->ctx); if (card->ops == NULL || card->ops->get_challenge == NULL) LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); 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, 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 || 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); /* 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, size_t count, unsigned long flags) { int r; if (card == NULL) { return SC_ERROR_INVALID_ARGUMENTS; } LOG_FUNC_CALLED(card->ctx); 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); } int sc_append_record(sc_card_t *card, const u8 * buf, size_t count, unsigned long flags) { int r; if (card == NULL) { return SC_ERROR_INVALID_ARGUMENTS; } LOG_FUNC_CALLED(card->ctx); 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, 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) { 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); /* 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) { int r; if (card == NULL) { return SC_ERROR_INVALID_ARGUMENTS; } LOG_FUNC_CALLED(card->ctx); 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); } int sc_card_ctl(sc_card_t *card, unsigned long cmd, void *args) { int r = SC_ERROR_NOT_SUPPORTED; if (card == NULL) { return SC_ERROR_INVALID_ARGUMENTS; } 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); /* suppress "not supported" error messages */ if (r == SC_ERROR_NOT_SUPPORTED) { sc_log(card->ctx, "card_ctl(%lu) not supported", cmd); return r; } LOG_FUNC_RETURN(card->ctx, r); } int _sc_card_add_algorithm(sc_card_t *card, const sc_algorithm_info_t *info) { sc_algorithm_info_t *p; if (info == NULL) { return SC_ERROR_INVALID_ARGUMENTS; } p = (sc_algorithm_info_t *) realloc(card->algorithms, (card->algorithm_count + 1) * sizeof(*info)); if (!p) { return SC_ERROR_OUT_OF_MEMORY; } card->algorithms = p; p += card->algorithm_count; card->algorithm_count++; *p = *info; return SC_SUCCESS; } int _sc_card_add_symmetric_alg(sc_card_t *card, unsigned int algorithm, unsigned int key_length, unsigned long flags) { sc_algorithm_info_t info; memset(&info, 0, sizeof(info)); info.algorithm = algorithm; info.key_length = key_length; info.flags = flags; return _sc_card_add_algorithm(card, &info); } 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, int algorithm) { sc_algorithm_info_t info; memset(&info, 0, sizeof(info)); sc_init_oid(&info.u._ec.params.id); info.algorithm = algorithm; info.key_length = key_length; info.flags = flags; info.u._ec.ext_flags = ext_flags; if (curve_oid) info.u._ec.params.id = *curve_oid; return _sc_card_add_algorithm(card, &info); } 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 (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->key_length == key_length) return info; } return NULL; } 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); } 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; memset(&info, 0, sizeof(info)); 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, 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, 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, const struct sc_atr_table *table, struct sc_atr *atr) { u8 *card_atr_bin; size_t card_atr_bin_len; char card_atr_hex[3 * SC_MAX_ATR_SIZE]; size_t card_atr_hex_len; unsigned int i = 0; if (ctx == NULL || table == NULL || atr == NULL) return -1; card_atr_bin = atr->value; card_atr_bin_len = atr->len; 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_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; const char *matr = table[i].atrmask; size_t tatr_len = strlen(tatr); u8 mbin[SC_MAX_ATR_SIZE], tbin[SC_MAX_ATR_SIZE]; size_t mbin_len, tbin_len, s, matr_len; size_t fix_hex_len = card_atr_hex_len; size_t fix_bin_len = card_atr_bin_len; sc_debug(ctx, SC_LOG_DEBUG_MATCH, "ATR try : %s", tatr); if (tatr_len != fix_hex_len) { sc_debug(ctx, SC_LOG_DEBUG_MATCH, "ignored - wrong length"); continue; } if (matr != NULL) { sc_debug(ctx, SC_LOG_DEBUG_MATCH, "ATR mask: %s", matr); matr_len = strlen(matr); if (tatr_len != matr_len) continue; tbin_len = sizeof(tbin); sc_hex_to_bin(tatr, tbin, &tbin_len); mbin_len = sizeof(mbin); sc_hex_to_bin(matr, mbin, &mbin_len); if (mbin_len != fix_bin_len) { 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++) { /* reduce tatr with mask */ tbin[s] = (tbin[s] & mbin[s]); /* create copy of card_atr_bin masked) */ mbin[s] = (card_atr_bin[s] & mbin[s]); } if (memcmp(tbin, mbin, tbin_len) != 0) continue; } else { if (strncasecmp(tatr, card_atr_hex, tatr_len) != 0) continue; } return i; } return -1; } int _sc_match_atr(sc_card_t *card, const struct sc_atr_table *table, int *type_out) { int res; if (card == NULL) return -1; res = match_atr_table(card->ctx, table, &card->atr); if (res < 0) return res; if (type_out != NULL) *type_out = table[res].type; return res; } scconf_block *_sc_match_atr_block(sc_context_t *ctx, struct sc_card_driver *driver, struct sc_atr *atr) { struct sc_card_driver *drv; struct sc_atr_table *table; int res; if (ctx == NULL) return NULL; if (driver) { drv = driver; table = drv->atr_map; res = match_atr_table(ctx, table, atr); if (res < 0) return NULL; return table[res].card_atr; } else { unsigned int i; for (i = 0; ctx->card_drivers[i] != NULL; i++) { drv = ctx->card_drivers[i]; table = drv->atr_map; res = match_atr_table(ctx, table, atr); if (res < 0) continue; return table[res].card_atr; } } return NULL; } int _sc_add_atr(sc_context_t *ctx, struct sc_card_driver *driver, struct sc_atr_table *src) { struct sc_atr_table *map, *dst; map = (struct sc_atr_table *) realloc(driver->atr_map, (driver->natrs + 2) * sizeof(struct sc_atr_table)); if (!map) return SC_ERROR_OUT_OF_MEMORY; driver->atr_map = map; dst = &driver->atr_map[driver->natrs++]; memset(dst, 0, sizeof(*dst)); memset(&driver->atr_map[driver->natrs], 0, sizeof(struct sc_atr_table)); dst->atr = strdup(src->atr); if (!dst->atr) return SC_ERROR_OUT_OF_MEMORY; if (src->atrmask) { dst->atrmask = strdup(src->atrmask); if (!dst->atrmask) return SC_ERROR_OUT_OF_MEMORY; } else { dst->atrmask = NULL; } if (src->name) { dst->name = strdup(src->name); if (!dst->name) return SC_ERROR_OUT_OF_MEMORY; } else { dst->name = NULL; } dst->type = src->type; dst->flags = src->flags; dst->card_atr = src->card_atr; return SC_SUCCESS; } int _sc_free_atr(sc_context_t *ctx, struct sc_card_driver *driver) { unsigned int i; for (i = 0; i < driver->natrs; i++) { struct sc_atr_table *src = &driver->atr_map[i]; if (src->atr) free((void *)src->atr); if (src->atrmask) free((void *)src->atrmask); if (src->name) free((void *)src->name); src->card_atr = NULL; src = NULL; } if (driver->atr_map) free(driver->atr_map); driver->atr_map = NULL; driver->natrs = 0; return SC_SUCCESS; } scconf_block *sc_get_conf_block(sc_context_t *ctx, const char *name1, const char *name2, int priority) { int i; scconf_block *conf_block = NULL; for (i = 0; ctx->conf_blocks[i] != NULL; i++) { scconf_block **blocks; blocks = scconf_find_blocks(ctx->conf, ctx->conf_blocks[i], name1, name2); if (blocks != NULL) { conf_block = blocks[0]; free(blocks); } if (conf_block != NULL && priority) break; } return conf_block; } void sc_invalidate_cache(struct sc_card *card) { if (card) { sc_file_free(card->cache.current_ef); sc_file_free(card->cache.current_df); memset(&card->cache, 0, sizeof(card->cache)); card->cache.valid = 0; } } 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; } 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)); } int sc_copy_ec_params(struct sc_ec_parameters *dst, struct sc_ec_parameters *src) { if (!dst || !src) return SC_ERROR_INVALID_ARGUMENTS; memset(dst, 0, sizeof(*dst)); if (src->named_curve) { dst->named_curve = strdup(src->named_curve); if (!dst->named_curve) return SC_ERROR_OUT_OF_MEMORY; } dst->id = src->id; if (src->der.value && src->der.len) { dst->der.value = malloc(src->der.len); 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; return SC_SUCCESS; } scconf_block * sc_match_atr_block(sc_context_t *ctx, struct sc_card_driver *driver, struct sc_atr *atr) { return _sc_match_atr_block(ctx, driver, atr); } #ifdef ENABLE_SM static int sc_card_sm_unload(struct sc_card *card) { if (card->sm_ctx.module.ops.module_cleanup) card->sm_ctx.module.ops.module_cleanup(card->ctx); if (card->sm_ctx.module.handle) sc_dlclose(card->sm_ctx.module.handle); card->sm_ctx.module.handle = NULL; return 0; } static int sc_card_sm_load(struct sc_card *card, const char *module_path, const char *in_module) { struct sc_context *ctx = NULL; int rv = SC_ERROR_INTERNAL; char *module = NULL; #ifdef _WIN32 char temp_path[PATH_MAX]; size_t temp_len; const char path_delim = '\\'; char expanded_val[PATH_MAX]; DWORD expanded_len; #else const char path_delim = '/'; #endif if (card == NULL) { return SC_ERROR_INVALID_ARGUMENTS; } ctx = card->ctx; LOG_FUNC_CALLED(ctx); if (!in_module) return sc_card_sm_unload(card); #ifdef _WIN32 if (!module_path || strlen(module_path) == 0) { temp_len = PATH_MAX-1; rv = sc_ctx_win32_get_config_value(NULL, "SmDir", "Software\\OpenSC Project\\OpenSC", temp_path, &temp_len); if (rv == SC_SUCCESS) { temp_path[temp_len] = '\0'; module_path = temp_path; } } expanded_len = PATH_MAX; expanded_len = ExpandEnvironmentStringsA(module_path, expanded_val, expanded_len); if (0 < expanded_len && expanded_len < sizeof expanded_val) module_path = expanded_val; #endif 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); } else { module = strdup(in_module); } if (!module) return SC_ERROR_OUT_OF_MEMORY; sc_log(ctx, "try to load SM module '%s'", module); do { struct sm_module_operations *mod_ops = &card->sm_ctx.module.ops; void *mod_handle; card->sm_ctx.module.handle = sc_dlopen(module); if (!card->sm_ctx.module.handle) { sc_log(ctx, "cannot open dynamic library '%s': %s", module, sc_dlerror()); break; } mod_handle = card->sm_ctx.module.handle; mod_ops->initialize = sc_dlsym(mod_handle, "initialize"); if (!mod_ops->initialize) { sc_log(ctx, "SM handler 'initialize' not exported: %s", sc_dlerror()); break; } mod_ops->get_apdus = sc_dlsym(mod_handle, "get_apdus"); if (!mod_ops->get_apdus) { sc_log(ctx, "SM handler 'get_apdus' not exported: %s", sc_dlerror()); break; } mod_ops->finalize = sc_dlsym(mod_handle, "finalize"); if (!mod_ops->finalize) sc_log(ctx, "SM handler 'finalize' not exported -- ignored"); mod_ops->module_init = sc_dlsym(mod_handle, "module_init"); if (!mod_ops->module_init) sc_log(ctx, "SM handler 'module_init' not exported -- ignored"); mod_ops->module_cleanup = sc_dlsym(mod_handle, "module_cleanup"); if (!mod_ops->module_cleanup) sc_log(ctx, "SM handler 'module_cleanup' not exported -- ignored"); mod_ops->test = sc_dlsym(mod_handle, "test"); if (mod_ops->test) sc_log(ctx, "SM handler 'test' not exported -- ignored"); rv = 0; break; } while(0); if (rv) sc_card_sm_unload(card); card->sm_ctx.sm_mode = SM_MODE_ACL; if (module) free(module); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, rv); } /* get SM related configuration settings and initialize SM session, SM module, ... */ static int sc_card_sm_check(struct sc_card *card) { const char *sm = NULL, *module_name = NULL, *module_path = NULL, *module_data = NULL, *sm_mode = NULL; struct sc_context *ctx = card->ctx; scconf_block *atrblock = NULL, *sm_conf_block = NULL; int rv, ii; LOG_FUNC_CALLED(ctx); /* get the name of card specific SM configuration section */ atrblock = _sc_match_atr_block(ctx, card->driver, &card->atr); if (atrblock == NULL) LOG_FUNC_RETURN(ctx, SC_SUCCESS); sm = scconf_get_str(atrblock, "secure_messaging", NULL); if (!sm) LOG_FUNC_RETURN(ctx, SC_SUCCESS); /* get SM configuration section by the name */ sc_log(ctx, "secure_messaging configuration block '%s'", sm); for (ii = 0; ctx->conf_blocks[ii]; ii++) { scconf_block **blocks; blocks = scconf_find_blocks(ctx->conf, ctx->conf_blocks[ii], "secure_messaging", sm); if (blocks) { sm_conf_block = blocks[0]; free(blocks); } if (sm_conf_block != NULL) break; } if (!sm_conf_block) 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", DEFAULT_SM_MODULE_PATH); 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"); rv = sc_card_sm_load(card, module_path, module_name); LOG_TEST_RET(ctx, rv, "Failed to load SM module"); strlcpy(card->sm_ctx.module.filename, module_name, sizeof(card->sm_ctx.module.filename)); strlcpy(card->sm_ctx.config_section, sm, sizeof(card->sm_ctx.config_section)); /* allocate resources for the external SM module */ if (card->sm_ctx.module.ops.module_init) { module_data = scconf_get_str(sm_conf_block, "module_data", NULL); rv = card->sm_ctx.module.ops.module_init(ctx, module_data); 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); 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"); card->sm_ctx.sm_mode = SM_MODE_TRANSMIT; rv = card->sm_ctx.ops.open(card); LOG_TEST_RET(ctx, rv, "Cannot initialize SM"); } sc_log(ctx, "SM mode:%X", card->sm_ctx.sm_mode); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, rv); } #endif OpenSC-0.26.1/src/libopensc/cardctl.h000066400000000000000000000653731474147347300173150ustar00rootroot00000000000000/* * 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 * 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 _OPENSC_CARDCTL_H #define _OPENSC_CARDCTL_H #include #include "libopensc/types.h" #ifdef __cplusplus extern "C" { #endif #define _CTL_PREFIX(a, b, c) (((a) << 24) | ((b) << 16) | ((c) << 8)) enum { /* * Generic card_ctl calls */ SC_CARDCTL_GENERIC_BASE = 0x00000000, SC_CARDCTL_ERASE_CARD, SC_CARDCTL_GET_DEFAULT_KEY, 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, SC_CARDCTL_GET_MODEL, /* * Cryptoflex specific calls */ SC_CARDCTL_CRYPTOFLEX_BASE = _CTL_PREFIX('C', 'F', 'X'), SC_CARDCTL_CRYPTOFLEX_GENERATE_KEY, /* * TCOS specific calls */ SC_CARDCTL_TCOS_BASE = _CTL_PREFIX('T', 'C', 'S'), SC_CARDCTL_TCOS_SETPERM, /* * CardOS specific calls * (formerly known as "etoken" driver, thus ETK as prefix) */ SC_CARDCTL_CARDOS_BASE = _CTL_PREFIX('E', 'T', 'K'), SC_CARDCTL_CARDOS_PUT_DATA_FCI, 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 */ SC_CARDCTL_STARCOS_BASE = _CTL_PREFIX('S', 'T', 'A'), SC_CARDCTL_STARCOS_CREATE_FILE, SC_CARDCTL_STARCOS_CREATE_END, SC_CARDCTL_STARCOS_WRITE_KEY, SC_CARDCTL_STARCOS_GENERATE_KEY, /* * Oberthur specific calls */ SC_CARDCTL_OBERTHUR_BASE = _CTL_PREFIX('O', 'B', 'R'), SC_CARDCTL_OBERTHUR_UPDATE_KEY, SC_CARDCTL_OBERTHUR_GENERATE_KEY, SC_CARDCTL_OBERTHUR_CREATE_PIN, /* * Setcos specific calls */ SC_CARDCTL_SETCOS_BASE = _CTL_PREFIX('S', 'E', 'T'), SC_CARDCTL_SETCOS_PUTDATA, SC_CARDCTL_SETCOS_GETDATA, SC_CARDCTL_SETCOS_GENERATE_STORE_KEY, SC_CARDCTL_SETCOS_ACTIVATE_FILE, /* * Muscle specific calls */ SC_CARDCTL_MUSCLE_BASE = _CTL_PREFIX('M', 'S', 'C'), SC_CARDCTL_MUSCLE_GENERATE_KEY, SC_CARDCTL_MUSCLE_EXTRACT_KEY, SC_CARDCTL_MUSCLE_IMPORT_KEY, SC_CARDCTL_MUSCLE_VERIFIED_PINS, /* * ASEPCOS specific calls */ 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 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, /* * EnterSafe specific calls */ SC_CARDCTL_ENTERSAFE_BASE = _CTL_PREFIX('E', 'S', 'F'), SC_CARDCTL_ENTERSAFE_CREATE_FILE, SC_CARDCTL_ENTERSAFE_CREATE_END, SC_CARDCTL_ENTERSAFE_WRITE_KEY, SC_CARDCTL_ENTERSAFE_GENERATE_KEY, SC_CARDCTL_ENTERSAFE_PREINSTALL_KEYS, /* * Rutoken ECP specific calls */ SC_CARDCTL_RTECP_BASE = _CTL_PREFIX('R', 'T', 'E'), SC_CARDCTL_RTECP_INIT, SC_CARDCTL_RTECP_INIT_END, SC_CARDCTL_RTECP_GENERATE_KEY, /* * Westcos specific */ SC_CARDCTL_WESTCOS_FREEZE = _CTL_PREFIX('W', 'T', 'C'), SC_CARDCTL_WESTCOS_CREATE_MF, SC_CARDCTL_WESTCOS_COMMIT, SC_CARDCTL_WESTCOS_ROLLBACK, SC_CARDCTL_WESTCOS_AUT_KEY, SC_CARDCTL_WESTCOS_CHANGE_KEY, SC_CARDCTL_WESTCOS_SET_DEFAULT_KEY, SC_CARDCTL_WESTCOS_LOAD_DATA, /* * MyEID specific calls */ SC_CARDCTL_MYEID_BASE = _CTL_PREFIX('M', 'Y', 'E'), SC_CARDCTL_MYEID_PUTDATA, SC_CARDCTL_MYEID_GETDATA, SC_CARDCTL_MYEID_GENERATE_STORE_KEY, SC_CARDCTL_MYEID_ACTIVATE_CARD, /* * PIV specific calls */ SC_CARDCTL_PIV_BASE = _CTL_PREFIX('P', 'I', 'V'), SC_CARDCTL_PIV_AUTHENTICATE, SC_CARDCTL_PIV_GENERATE_KEY, SC_CARDCTL_PIV_PIN_PREFERENCE, SC_CARDCTL_PIV_OBJECT_PRESENT, /* * CAC specific calls */ SC_CARDCTL_CAC_BASE = _CTL_PREFIX('C', 'A', 'C'), SC_CARDCTL_CAC_INIT_GET_GENERIC_OBJECTS, SC_CARDCTL_CAC_GET_NEXT_GENERIC_OBJECT, SC_CARDCTL_CAC_FINAL_GET_GENERIC_OBJECTS, 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, /* * Coolkey specific calls */ SC_CARDCTL_COOLKEY_BASE = _CTL_PREFIX('C', 'O', 'K'), SC_CARDCTL_COOLKEY_INIT_GET_OBJECTS, SC_CARDCTL_COOLKEY_GET_NEXT_OBJECT, SC_CARDCTL_COOLKEY_FINAL_GET_OBJECTS, SC_CARDCTL_COOLKEY_GET_ATTRIBUTE, 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_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_PUT_DATA, SC_CARDCTL_IASECC_SDO_KEY_RSA_PUT_DATA, SC_CARDCTL_IASECC_SDO_GET_DATA, SC_CARDCTL_IASECC_SDO_GENERATE, SC_CARDCTL_IASECC_SDO_CREATE, SC_CARDCTL_IASECC_SDO_DELETE, /* * OpenPGP */ 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 */ SC_CARDCTL_SC_HSMP_BASE = _CTL_PREFIX('S', 'C', 'H'), SC_CARDCTL_SC_HSM_GENERATE_KEY, SC_CARDCTL_SC_HSM_INITIALIZE, 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_GENERATE_KEY, SC_CARDCTL_DNIE_GET_INFO, /* * isoApplet Java Card Applet */ 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_GET_ALL_CONTAINERS, SC_CARDCTL_GIDS_GET_CONTAINER_DETAIL, SC_CARDCTL_GIDS_SELECT_KEY_REFERENCE, SC_CARDCTL_GIDS_CREATE_KEY, SC_CARDCTL_GIDS_GENERATE_KEY, SC_CARDCTL_GIDS_IMPORT_KEY, SC_CARDCTL_GIDS_SAVE_CERT, SC_CARDCTL_GIDS_DELETE_KEY, SC_CARDCTL_GIDS_DELETE_CERT, 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 { SC_CARDCTRL_LIFECYCLE_ADMIN, SC_CARDCTRL_LIFECYCLE_USER, SC_CARDCTRL_LIFECYCLE_OTHER }; /* * Generic cardctl - check if the required key is a default * key (such as the GPK "TEST KEYTEST KEY" key, or the Cryptoflex AAK) */ struct sc_cardctl_default_key { int method; /* SC_AC_XXX */ int key_ref; /* key reference */ size_t len; /* in: max size, out: actual size */ u8 * key_data; /* out: key data */ }; /* * Generic cardctl - initialize token using PKCS#11 style */ typedef struct sc_cardctl_pkcs11_init_token { const unsigned char * so_pin; size_t so_pin_len; const char * label; } sc_cardctl_pkcs11_init_token_t; /* * Generic cardctl - set pin using PKCS#11 style */ typedef struct sc_cardctl_pkcs11_init_pin { const unsigned char * pin; size_t pin_len; } sc_cardctl_pkcs11_init_pin_t; /* * Generic cardctl - card driver can examine token info */ struct sc_cardctl_parsed_token_info { unsigned int flags; struct sc_pkcs15_tokeninfo * tokeninfo; }; /* * Siemens CardOS PIN info */ struct sc_cardctl_cardos_obj_info { u8 * data; size_t len; }; struct sc_cardctl_cardos_genkey_info { unsigned int key_id; size_t 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 */ }; /* * Cryptoflex info */ struct sc_cardctl_cryptoflex_genkey_info { unsigned int key_num; size_t key_bits; unsigned long exponent; unsigned char * pubkey; unsigned int pubkey_len; }; /* * Starcos stuff */ #define SC_STARCOS_MF_DATA 0x01 #define SC_STARCOS_DF_DATA 0x02 #define SC_STARCOS_EF_DATA 0x04 typedef struct sc_starcos_create_data_st { unsigned int type; union { struct { u8 header[19]; /* see starcos manual */ } mf; struct { u8 header[25]; /* see starcos manual */ u8 size[2]; } df; struct { u8 header[16]; /* see starcos manual */ } ef; } data; } sc_starcos_create_data; typedef struct sc_starcos_write_key_data_st { u8 mode; /* 1 = Update, 0 = Install */ u8 kid; /* key id */ u8 key_header[12]; /* see starcos manual */ const u8 *key; size_t key_len; } sc_starcos_wkey_data; typedef struct sc_starcos_gen_key_data_st { u8 key_id; size_t key_length; u8 *modulus; } sc_starcos_gen_key_data; /* * Oberthur ex_data stuff */ enum SC_CARDCTL_OBERTHUR_KEY_TYPE { SC_CARDCTL_OBERTHUR_KEY_DES = 0x80, SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC = 0xA1, SC_CARDCTL_OBERTHUR_KEY_RSA_SFM, SC_CARDCTL_OBERTHUR_KEY_RSA_CRT, SC_CARDCTL_OBERTHUR_KEY_EC_CRT, SC_CARDCTL_OBERTHUR_KEY_EC_PUBLIC }; struct sc_cardctl_oberthur_genkey_info { unsigned int id_prv, id_pub; size_t key_bits; unsigned long exponent; unsigned char * pubkey; size_t pubkey_len; int method; /* SC_AC_XXX */ int key_ref; /* key reference */ }; struct sc_cardctl_oberthur_updatekey_info { enum SC_CARDCTL_OBERTHUR_KEY_TYPE type; unsigned char *data; unsigned int data_len; unsigned char id[256]; size_t id_len; }; struct sc_cardctl_oberthur_createpin_info { unsigned int type; unsigned int ref; const unsigned char *pin; size_t pin_len; unsigned int pin_tries; const unsigned char *puk; unsigned int puk_len; unsigned int puk_tries; }; /* * Setcos stuff */ struct sc_cardctl_setcos_data_obj { int P1; int P2; u8 * Data; size_t DataLen; int LengthMax; }; struct sc_cardctl_setcos_gen_store_key_info { int op_type; size_t mod_len; /* in bits */ size_t pubexp_len; /* in bits */ unsigned char *pubexp; size_t primep_len; /* in bits */ unsigned char *primep; size_t primeq_len; /* in bits */ unsigned char *primeq; }; /* * Muscle stuff */ typedef struct sc_cardctl_muscle_gen_key_info { int keyType; size_t keySize; int privateKeyLocation; int publicKeyLocation; } sc_cardctl_muscle_gen_key_info_t; typedef struct sc_cardctl_muscle_key_info { int keyType; int keyLocation; size_t keySize; size_t modLength; u8* modValue; size_t expLength; u8* expValue; size_t pLength; u8* pValue; size_t qLength; u8* qValue; size_t pqLength; u8* pqValue; size_t dp1Length; u8* dp1Value; size_t dq1Length; u8* dq1Value; size_t gLength; u8* gValue; size_t yLength; u8* yValue; } sc_cardctl_muscle_key_info_t; typedef struct sc_cardctl_muscle_verified_pins_info { unsigned verifiedPins; } sc_cardctl_muscle_verified_pins_info_t; /* ASEPCOS ctl specific structures */ typedef struct sc_cardctl_asepcos_change_key { const u8 *data; size_t datalen; } sc_cardctl_asepcos_change_key_t; typedef struct sc_cardctl_asepcos_akn2fileid { int akn; int fileid; } sc_cardctl_asepcos_akn2fileid_t; typedef struct sc_cardctl_asepcos_activate_file { int fileid; int is_ef; } sc_cardctl_asepcos_activate_file_t; #define OP_TYPE_GENERATE 0 #define OP_TYPE_STORE 1 /* * Westcos */ typedef struct { int key_reference; size_t key_len; /* 8, 16 or 24 */ u8 key_value[24]; }sc_autkey_t; typedef struct { sc_autkey_t master_key; sc_autkey_t new_key; u8 key_template[7]; }sc_changekey_t; /* * RuToken types and constants */ #define SC_RUTOKEN_DO_PART_BODY_LEN 199 #define SC_RUTOKEN_DO_HDR_LEN 32 /* DO Types */ #define SC_RUTOKEN_TYPE_MASK 0xF #define SC_RUTOKEN_TYPE_SE 0x0 #define SC_RUTOKEN_TYPE_CHV 0x1 #define SC_RUTOKEN_TYPE_KEY 0x2 #define SC_RUTOKEN_COMPACT_DO_MAX_LEN 16 /* MAX Body length of Compact DOs */ #define SC_RUTOKEN_DO_ALL_MIN_ID 0x1 /* MIN ID value of All DOs */ #define SC_RUTOKEN_DO_CHV_MAX_ID 0x1F /* MAX ID value of CHV-objects */ #define SC_RUTOKEN_DO_NOCHV_MAX_ID 0x7F /* MAX ID value of All Other DOs */ /* DO Default Lengths */ #define SC_RUTOKEN_DEF_LEN_DO_GOST 32 #define SC_RUTOKEN_DEF_LEN_DO_SE 6 #define SC_RUTOKEN_ALLTYPE_SE SC_RUTOKEN_TYPE_SE /* SE */ #define SC_RUTOKEN_ALLTYPE_GCHV SC_RUTOKEN_TYPE_CHV /* GCHV */ #define SC_RUTOKEN_ALLTYPE_LCHV 0x11 /* LCHV */ #define SC_RUTOKEN_ALLTYPE_GOST SC_RUTOKEN_TYPE_KEY /* GOST */ /* 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 */ /* DO Options */ #define SC_RUTOKEN_OPTIONS_GCHV_ACCESS_MASK 0x7 /* Access rights */ #define SC_RUTOKEN_OPTIONS_GACCESS_ADMIN SC_RUTOKEN_DEF_ID_GCHV_ADMIN /* ADMIN */ #define SC_RUTOKEN_OPTIONS_GACCESS_USER SC_RUTOKEN_DEF_ID_GCHV_USER /* USER */ #define SC_RUTOKEN_OPTIONS_GOST_CRYPT_MASK 0x7 /* crypto algorithm */ #define SC_RUTOKEN_OPTIONS_GOST_CRYPT_PZ 0x0 /* (encryptECB) simple-change mode */ #define SC_RUTOKEN_OPTIONS_GOST_CRYPT_GAMM 0x1 /* (encryptCNT) gamma mode */ #define SC_RUTOKEN_OPTIONS_GOST_CRYPT_GAMMOS 0x2 /* (encryptCFB) feed-back gamma mode */ /* DO flags */ #define SC_RUTOKEN_FLAGS_COMPACT_DO 0x1 #define SC_RUTOKEN_FLAGS_OPEN_DO_MASK 0x6 #define SC_RUTOKEN_FLAGS_BLEN_OPEN_DO 0x2 #define SC_RUTOKEN_FLAGS_FULL_OPEN_DO 0x6 /* DO MAX:CUR try */ #define SC_RUTOKEN_MAXTRY_MASK 0xF0 /* MAX try */ #define SC_RUTOKEN_CURTRY_MASK 0x0F /* CUR try */ #define SC_RUTOKEN_DO_CHV_MAX_ID_V2 SC_RUTOKEN_DEF_ID_GCHV_USER /* MAX ID value of CHV-objects */ #define SC_RUTOKEN_DO_NOCHV_MAX_ID_V2 SC_RUTOKEN_DO_NOCHV_MAX_ID /* MAX ID value of All Other DOs */ #if defined(__APPLE__) || defined(sun) #pragma pack(1) #else #pragma pack(push, 1) #endif typedef u8 sc_SecAttrV2_t[40]; typedef struct sc_ObjectTypeID{ u8 byObjectType; u8 byObjectID; } sc_ObjectTypeID_t; typedef struct sc_ObjectParams{ u8 byObjectOptions; u8 byObjectFlags; u8 byObjectTry; } sc_ObjectParams_t; typedef struct sc_DOHdrV2 { unsigned short wDOBodyLen; sc_ObjectTypeID_t OTID; sc_ObjectParams_t OP; u8 dwReserv1[4]; u8 abyReserv2[6]; sc_SecAttrV2_t SA_V2; } sc_DOHdrV2_t; typedef struct sc_DO_V2 { sc_DOHdrV2_t HDR; u8 abyDOBody[SC_RUTOKEN_DO_PART_BODY_LEN]; } sc_DO_V2_t; typedef enum { select_first, select_by_id, select_next } SC_RUTOKEN_DO_SEL_TYPES; typedef struct sc_DO_INFO_V2 { u8 DoId; SC_RUTOKEN_DO_SEL_TYPES SelType; u8 pDoData[256]; } sc_DO_INFO_t; struct sc_rutoken_decipherinfo { const u8 *inbuf; size_t inlen; u8 *outbuf; size_t outlen; }; /* * EnterSafe stuff * */ #define SC_ENTERSAFE_MF_DATA 0x01 #define SC_ENTERSAFE_DF_DATA 0x02 #define SC_ENTERSAFE_EF_DATA 0x04 #define ENTERSAFE_USER_PIN_ID 0x01 #define ENTERSAFE_SO_PIN_ID 0x02 #define ENTERSAFE_MIN_KEY_ID 0x01 #define ENTERSAFE_MAX_KEY_ID 0x09 #define ENTERSAFE_AC_EVERYONE 0x00 #define ENTERSAFE_AC_USER 0x04 #define ENTERSAFE_AC_NEVER 0xC0 #define ENTERSAFE_AC_ALWAYS 0x10 #define ENTERSAFE_AC_CHV 0x30 typedef struct sc_entersafe_create_data_st { unsigned int type; union { struct { u8 file_id[2]; u8 file_count; u8 flag; u8 ikf_size[2]; u8 create_ac; u8 append_ac; u8 lock_ac; u8 aid[16]; u8 init_key[16]; } df; struct { u8 file_id[2]; u8 size[2]; u8 attr[2]; u8 name; u8 ac[10]; u8 sm[2]; } ef; } data; } sc_entersafe_create_data; typedef struct sc_entersafe_wkey_data_st { u8 key_id; u8 usage; union{ struct sc_pkcs15_prkey_rsa* rsa; struct{ u8 EC; u8 ver; u8 key_val[256]; size_t key_len; } symmetric; }key_data; } sc_entersafe_wkey_data; typedef struct sc_entersafe_gen_key_data_st { u8 key_id; size_t key_length; u8 *modulus; } sc_entersafe_gen_key_data; #define SC_EPASS2003_KEY 0x00000010 #define SC_EPASS2003_KEY_RSA 0x00000011 #define SC_EPASS2003_SECRET 0x00000020 #define SC_EPASS2003_SECRET_PRE 0x00000021 #define SC_EPASS2003_SECRET_PIN 0x00000022 #define EPASS2003_AC_EVERYONE 0x00 #define EPASS2003_AC_USER 0x06 #define EPASS2003_AC_SO 0x08 #define EPASS2003_AC_NOONE 0x0F #define EPASS2003_AC_MAC_UNEQUAL 0x80 #define EPASS2003_AC_MAC_NOLESS 0x90 #define EPASS2003_AC_MAC_LESS 0xA0 #define EPASS2003_AC_MAC_EQUAL 0xB0 #define FID_STEP 0x20 typedef struct sc_epass2003_wkey_data_st { u8 type; union { struct { unsigned short fid; struct sc_pkcs15_prkey_rsa* rsa; } es_key; struct { u8 kid; u8 EC; u8 ac[2]; u8 key_val[256]; size_t key_len; } es_secret; } key_data; } sc_epass2003_wkey_data; typedef struct sc_epass2003_gen_key_data_st { int prkey_id; int pukey_id; size_t key_length; u8 *modulus; size_t modulus_len; } sc_epass2003_gen_key_data; #if defined(__APPLE__) || defined(sun) #pragma pack() #else #pragma pack(pop) #endif /* * Rutoken ECP stuff */ #define SC_RTECP_SEC_ATTR_SIZE 15 typedef struct sc_rtecp_genkey_data { unsigned int type; unsigned int key_id; union { struct { unsigned char *exponent; size_t exponent_len; unsigned char *modulus; size_t modulus_len; } rsa; struct { unsigned char *xy; size_t xy_len; } gostr3410; } u; } sc_rtecp_genkey_data_t; /* * MyEID stuff */ enum SC_CARDCTL_MYEID_KEY_TYPE { 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_GENERIC_SECRET = 0x41 }; struct sc_cardctl_myeid_data_obj { int P1; int P2; u8 * Data; size_t DataLen; int LengthMax; }; struct sc_cardctl_myeid_gen_store_key_info { int op_type; unsigned int key_type; /* value of SC_CARDCTL_MYEID_KEY_TYPE */ size_t key_len_bits; unsigned char *mod; size_t pubexp_len; unsigned char *pubexp; size_t primep_len; unsigned char *primep; size_t primeq_len; unsigned char *primeq; size_t dp1_len; unsigned char *dp1; size_t dq1_len; unsigned char *dq1; size_t invq_len; unsigned char *invq; /* new for MyEID > 3.6.0 */ unsigned char *d; /* EC private key / Symmetric key */ size_t d_len; /* EC / Symmetric */ unsigned char *ecpublic_point; /* EC public key */ size_t ecpublic_point_len; /* EC */ }; /* * PIV info */ 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_bits; /* RSA */ unsigned char * exponent; /* RSA */ size_t exponent_len; /* RSA */ unsigned char * pubkey; /* RSA */ size_t pubkey_len; /* RSA */ unsigned char * ecparam; /* EC */ unsigned int ecparam_len; /* EC */ unsigned char * ecpoint; /* EC */ size_t ecpoint_len; /* EC */ } sc_cardctl_piv_genkey_info_t; /* * OpenPGP */ #define SC_OPENPGP_KEY_SIGN 1 #define SC_OPENPGP_KEY_ENCR 2 #define SC_OPENPGP_KEY_AUTH 3 #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_keygen_info { u8 key_id; /* SC_OPENPGP_KEY_... */ u8 algorithm; /* SC_OPENPGP_KEYALGO_... */ union { struct { u8 keyformat; /* SC_OPENPGP_KEYFORMAT_RSA_... */ 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; /* Length of exponent in bit */ } rsa; struct { u8 keyformat; /* SC_OPENPGP_KEYFORMAT_EC_... */ u8 *ecpoint; size_t ecpoint_len; struct sc_object_id oid; u8 oid_len; size_t key_length; } ec; } u; } sc_cardctl_openpgp_keygen_info_t; typedef struct sc_cardctl_openpgp_keystore_info { u8 key_id; /* SC_OPENPGP_KEY_... */ u8 algorithm; /* SC_OPENPGP_KEYALGO_... */ union { struct { u8 keyformat; /* SC_OPENPGP_KEYFORMAT_RSA_... */ u8 *e; size_t e_len; /* Length of exponent in bit */ u8 *p; size_t p_len; u8 *q; size_t q_len; u8 *n; size_t n_len; } rsa; struct { u8 keyformat; /* SC_OPENPGP_KEYFORMAT_EC_... */ u8 *privateD; size_t privateD_len; u8 *ecpointQ; size_t ecpointQ_len; struct sc_object_id oid; u8 oid_len; } ec; } u; time_t creationtime; } sc_cardctl_openpgp_keystore_info_t; /* * SmartCard-HSM */ typedef struct sc_cardctl_sc_hsm_keygen_info { u8 key_id; u8 auth_key_id; /* Key to use for CV request signing */ u8 *gakprequest; /* GENERATE ASYMMETRIC KEY PAIR request */ size_t gakprequest_len; /* Size of request */ u8 *gakpresponse; /* Authenticated CV request, allocated by the driver */ size_t gakpresponse_len; /* Size of response */ } sc_cardctl_sc_hsm_keygen_info_t; typedef struct sc_cardctl_sc_hsm_init_param { u8 init_code[8]; /* Initialization code */ 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; typedef struct sc_cardctl_sc_hsm_dkek { int importShare; /* True to import share, false to just query status */ u8 dkek_share[32]; /* AES-256 DKEK share */ u8 dkek_shares; /* Total number of shares */ u8 outstanding_shares; /* Number of shares to be presented */ u8 key_check_value[8]; /* Key check value for DKEK */ } sc_cardctl_sc_hsm_dkek_t; typedef struct sc_cardctl_sc_hsm_wrapped_key { u8 key_id; /* Key identifier */ u8 *wrapped_key; /* Binary 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 { struct sc_lv_data prime; struct sc_lv_data coefficientA; struct sc_lv_data coefficientB; struct sc_lv_data basePointG; struct sc_lv_data order; struct sc_lv_data coFactor; } sc_cardctl_isoApplet_ec_parameters_t; typedef struct sc_cardctl_isoApplet_genkey { u8 algorithm_ref; /* Algorithm reference sent to card */ unsigned int priv_key_ref; /* Private key reference sent to card */ union { struct { struct sc_lv_data modulus; struct sc_lv_data exponent; } rsa; struct { sc_cardctl_isoApplet_ec_parameters_t params; struct sc_lv_data ecPointQ; } ec; } pubkey; } sc_cardctl_isoApplet_genkey_t; typedef struct sc_cardctl_isoApplet_import_key { u8 algorithm_ref; /* Algorithm reference sent to card */ unsigned int priv_key_ref; /* Private key reference sent to card */ union { struct { struct sc_lv_data p; struct sc_lv_data q; struct sc_lv_data iqmp; struct sc_lv_data dmp1; struct sc_lv_data dmq1; } rsa; struct { sc_cardctl_isoApplet_ec_parameters_t params; struct sc_lv_data privateD; } ec; } privkey; } sc_cardctl_isoApplet_import_key_t; /* * coolkey object returned from the card control interface */ typedef struct sc_cardctl_coolkey_object { sc_path_t path; unsigned long id; size_t length; u8 *data; } sc_cardctl_coolkey_object_t; /* data structure to pass attributes through the ctl interface */ typedef struct sc_cardctl_coolkey_attribute { const sc_cardctl_coolkey_object_t *object; unsigned long attribute_type; u8 attribute_data_type; size_t attribute_length; const u8 *attribute_value; } sc_cardctl_coolkey_attribute_t; #define SC_CARDCTL_COOLKEY_ATTR_TYPE_STRING 0 #define SC_CARDCTL_COOLKEY_ATTR_TYPE_ULONG 1 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 parameter */ int template_count; /* in parameter */ sc_cardctl_coolkey_object_t *obj; /* out parameter */ } sc_cardctl_coolkey_find_object_t; #define SC_CARDCTL_COOLKEY_FIND_BY_ID 0 #define SC_CARDCTL_COOLKEY_FIND_BY_TEMPLATE 1 #ifdef __cplusplus } #endif #endif /* _OPENSC_CARDCTL_H */ OpenSC-0.26.1/src/libopensc/cards.h000066400000000000000000000224221474147347300167610ustar00rootroot00000000000000/* * cards.h: Registered card types for sc_card_t->type * * Copyright (C) 2005 Antti Tapaninen * * 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 _OPENSC_CARDS_H #define _OPENSC_CARDS_H #include "libopensc/types.h" #ifdef __cplusplus extern "C" { #endif enum { /* Generic card types */ SC_CARD_TYPE_UNKNOWN = -1, SC_CARD_TYPE_GENERIC_BASE = 0, SC_CARD_TYPE_GENERIC, /* Cards without registered type, yet */ SC_CARD_TYPE_TEST_BASE = 500, /* cardos driver */ SC_CARD_TYPE_CARDOS_BASE = 1000, SC_CARD_TYPE_CARDOS_GENERIC, SC_CARD_TYPE_CARDOS_M4_01, SC_CARD_TYPE_CARDOS_M4_2, SC_CARD_TYPE_CARDOS_M4_3, SC_CARD_TYPE_CARDOS_M4_2B, /* 4.2b is after 4.3b */ SC_CARD_TYPE_CARDOS_M4_2C, 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, SC_CARD_TYPE_FLEX_GENERIC, SC_CARD_TYPE_FLEX_CRYPTO, SC_CARD_TYPE_FLEX_MULTI, SC_CARD_TYPE_FLEX_CYBER, /* mcrd driver */ SC_CARD_TYPE_MCRD_BASE = 5000, SC_CARD_TYPE_MCRD_GENERIC, /* 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, /* starcos driver */ 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, SC_CARD_TYPE_TCOS_GENERIC, SC_CARD_TYPE_TCOS_V2, SC_CARD_TYPE_TCOS_V3, /* openpgp driver */ 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, /* oberthur driver */ SC_CARD_TYPE_OBERTHUR_BASE = 11000, SC_CARD_TYPE_OBERTHUR_GENERIC, SC_CARD_TYPE_OBERTHUR_32K, 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, /* belpic driver */ SC_CARD_TYPE_BELPIC_BASE = 12000, SC_CARD_TYPE_BELPIC_GENERIC, SC_CARD_TYPE_BELPIC_EID, /* 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, /* MuscleApplet */ SC_CARD_TYPE_MUSCLE_BASE = 15000, SC_CARD_TYPE_MUSCLE_GENERIC, SC_CARD_TYPE_MUSCLE_V1, SC_CARD_TYPE_MUSCLE_V2, SC_CARD_TYPE_MUSCLE_ETOKEN_72K, SC_CARD_TYPE_MUSCLE_JCOP241, SC_CARD_TYPE_MUSCLE_JCOP242R2_NO_EXT_APDU, /* Athena APCOS cards */ SC_CARD_TYPE_ASEPCOS_BASE = 17000, SC_CARD_TYPE_ASEPCOS_GENERIC, SC_CARD_TYPE_ASEPCOS_JAVA, /* EnterSafe cards */ SC_CARD_TYPE_ENTERSAFE_BASE = 19000, SC_CARD_TYPE_ENTERSAFE_3K, SC_CARD_TYPE_ENTERSAFE_FTCOS_PK_01C, SC_CARD_TYPE_ENTERSAFE_FTCOS_EPASS2003, SC_CARD_TYPE_ENTERSAFE_EJAVA_PK_01C, SC_CARD_TYPE_ENTERSAFE_EJAVA_PK_01C_T0, SC_CARD_TYPE_ENTERSAFE_EJAVA_H10CR_PK_01C_T1, SC_CARD_TYPE_ENTERSAFE_EJAVA_D11CR_PK_01C_T1, SC_CARD_TYPE_ENTERSAFE_EJAVA_C21C_PK_01C_T1, SC_CARD_TYPE_ENTERSAFE_EJAVA_A22CR_PK_01C_T1, SC_CARD_TYPE_ENTERSAFE_EJAVA_A40CR_PK_01C_T1, /* 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, SC_CARD_TYPE_GEMSAFEV1_GENERIC, SC_CARD_TYPE_GEMSAFEV1_PTEID, SC_CARD_TYPE_GEMSAFEV1_SEEID, /* Italian CNS cards */ 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, /* Generic JavaCards without supported applet */ SC_CARD_TYPE_JAVACARD_BASE = 24000, SC_CARD_TYPE_JAVACARD, /* IAS/ECC cards */ SC_CARD_TYPE_IASECC_BASE = 25000, SC_CARD_TYPE_IASECC_GEMALTO, SC_CARD_TYPE_IASECC_OBERTHUR, 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_TERMINATED, /* ATR LC byte: 0F */ /* JavaCards with isoApplet */ SC_CARD_TYPE_ISO_APPLET_BASE = 28000, SC_CARD_TYPE_ISO_APPLET_GENERIC, /* Masktech cards */ SC_CARD_TYPE_MASKTECH_BASE = 29000, SC_CARD_TYPE_MASKTECH_GENERIC, /* GIDS cards */ SC_CARD_TYPE_GIDS_BASE = 30000, SC_CARD_TYPE_GIDS_GENERIC, SC_CARD_TYPE_GIDS_V1, SC_CARD_TYPE_GIDS_V2, /* JPKI cards */ SC_CARD_TYPE_JPKI_BASE = 31000, /* Coolkey cards */ SC_CARD_TYPE_COOLKEY_BASE = 32000, SC_CARD_TYPE_COOLKEY_GENERIC, /* CAC cards */ SC_CARD_TYPE_CAC_BASE = 33000, 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, /* 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_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, /* 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, }; 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_gemsafeV1_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_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_piv_driver(void); extern sc_card_driver_t *sc_get_muscle_driver(void); extern sc_card_driver_t *sc_get_asepcos_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_myeid_driver(void); extern sc_card_driver_t *sc_get_sc_hsm_driver(void); extern sc_card_driver_t *sc_get_itacns_driver(void); extern sc_card_driver_t *sc_get_authentic_driver(void); extern sc_card_driver_t *sc_get_iasecc_driver(void); extern sc_card_driver_t *sc_get_epass2003_driver(void); extern sc_card_driver_t *sc_get_dnie_driver(void); extern sc_card_driver_t *sc_get_isoApplet_driver(void); extern sc_card_driver_t *sc_get_masktech_driver(void); 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_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); #ifdef __cplusplus } #endif #endif /* _OPENSC_CARDS_H */ OpenSC-0.26.1/src/libopensc/ccid-types.h000066400000000000000000000202021474147347300177230ustar00rootroot00000000000000/* * Copyright (C) 2009-2015 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 */ /** * @file */ #ifndef _CCID_TYPES_H #define _CCID_TYPES_H #include #ifdef __cplusplus extern "C" { #endif #ifdef _MSC_VER #define PACKED #pragma pack(push,1) #elif defined(__GNUC__) #define PACKED __attribute__ ((__packed__)) #endif #define USB_REQ_CCID 0xA1 #define CCID_CONTROL_ABORT 0x01 #define CCID_CONTROL_GET_CLOCK_FREQUENCIES 0x02 #define CCID_CONTROL_GET_DATA_RATES 0x03 #define CCID_OPERATION_VERIFY 0x00; #define CCID_OPERATION_MODIFY 0x01; #define CCID_ENTRY_VALIDATE 0x02 #define CCID_BERROR_CMD_ABORTED 0xff /** Host aborted the current activity */ #define CCID_BERROR_ICC_MUTE 0xfe /** CCID timed out while talking to the ICC */ #define CCID_BERROR_XFR_PARITY_ERROR 0xfd /** Parity error while talking to the ICC */ #define CCID_BERROR_XFR_OVERRUN 0xfc /** Overrun error while talking to the ICC */ #define CCID_BERROR_HW_ERROR 0xfb /** An all inclusive hardware error occurred */ #define CCID_BERROR_BAD_ATR_TS 0xf #define CCID_BERROR_BAD_ATR_TCK 0xf #define CCID_BERROR_ICC_PROTOCOL_NOT_SUPPORTED 0xf6 #define CCID_BERROR_ICC_CLASS_NOT_SUPPORTED 0xf5 #define CCID_BERROR_PROCEDURE_BYTE_CONFLICT 0xf4 #define CCID_BERROR_DEACTIVATED_PROTOCOL 0xf3 #define CCID_BERROR_BUSY_WITH_AUTO_SEQUENCE 0xf2 /** Automatic Sequence Ongoing */ #define CCID_BERROR_PIN_TIMEOUT 0xf0 #define CCID_BERROR_PIN_CANCELLED 0xef #define CCID_BERROR_CMD_SLOT_BUSY 0xe0 /** A second command was sent to a slot which was already processing a command. */ #define CCID_BERROR_CMD_NOT_SUPPORTED 0x00 #define CCID_BERROR_OK 0x00 #define CCID_BSTATUS_OK_ACTIVE 0x00 /** No error. An ICC is present and active */ #define CCID_BSTATUS_OK_INACTIVE 0x01 /** No error. ICC is present and inactive */ #define CCID_BSTATUS_OK_NOICC 0x02 /** No error. No ICC is present */ #define CCID_BSTATUS_ERROR_ACTIVE 0x40 /** Failed. An ICC is present and active */ #define CCID_BSTATUS_ERROR_INACTIVE 0x41 /** Failed. ICC is present and inactive */ #define CCID_BSTATUS_ERROR_NOICC 0x42 /** Failed. No ICC is present */ #define CCID_WLEVEL_DIRECT __constant_cpu_to_le16(0) /** APDU begins and ends with this command */ #define CCID_WLEVEL_CHAIN_NEXT_XFRBLOCK __constant_cpu_to_le16(1) /** APDU begins with this command, and continue in the next PC_to_RDR_XfrBlock */ #define CCID_WLEVEL_CHAIN_END __constant_cpu_to_le16(2) /** abData field continues a command APDU and ends the APDU command */ #define CCID_WLEVEL_CHAIN_CONTINUE __constant_cpu_to_le16(3) /** abData field continues a command APDU and another block is to follow */ #define CCID_WLEVEL_RESPONSE_IN_DATABLOCK __constant_cpu_to_le16(0x10) /** empty abData field, continuation of response APDU is expected in the next RDR_to_PC_DataBlock */ #define CCID_PIN_ENCODING_BIN 0x00 #define CCID_PIN_ENCODING_BCD 0x01 #define CCID_PIN_ENCODING_ASCII 0x02 #define CCID_PIN_UNITS_BYTES 0x80 #define CCID_PIN_JUSTIFY_RIGHT 0x04 #define CCID_PIN_CONFIRM_NEW 0x01 #define CCID_PIN_INSERT_OLD 0x02 #define CCID_PIN_NO_MSG 0x00 #define CCID_PIN_MSG1 0x01 #define CCID_PIN_MSG2 0x02 #define CCID_PIN_MSG_REF 0x03 #define CCID_PIN_MSG_DEFAULT 0xff #define CCID_SLOTS_UNCHANGED 0x00 #define CCID_SLOT1_CARD_PRESENT 0x01 #define CCID_SLOT1_CHANGED 0x02 #define CCID_SLOT2_CARD_PRESENT 0x04 #define CCID_SLOT2_CHANGED 0x08 #define CCID_SLOT3_CARD_PRESENT 0x10 #define CCID_SLOT3_CHANGED 0x20 #define CCID_SLOT4_CARD_PRESENT 0x40 #define CCID_SLOT4_CHANGED 0x80 #define CCID_EXT_APDU_MAX (4 + 3 + 0xffff + 3) #define CCID_SHORT_APDU_MAX (4 + 1 + 0xff + 1) struct ccid_class_descriptor { uint8_t bLength; uint8_t bDescriptorType; uint16_t bcdCCID; uint8_t bMaxSlotIndex; uint8_t bVoltageSupport; uint32_t dwProtocols; uint32_t dwDefaultClock; uint32_t dwMaximumClock; uint8_t bNumClockSupport; uint32_t dwDataRate; uint32_t dwMaxDataRate; uint8_t bNumDataRatesSupported; uint32_t dwMaxIFSD; uint32_t dwSynchProtocols; uint32_t dwMechanical; uint32_t dwFeatures; uint32_t dwMaxCCIDMessageLength; uint8_t bClassGetResponse; uint8_t bclassEnvelope; uint16_t wLcdLayout; uint8_t bPINSupport; uint8_t bMaxCCIDBusySlots; } PACKED; typedef struct { uint8_t bmFindexDindex; uint8_t bmTCCKST0; uint8_t bGuardTimeT0; uint8_t bWaitingIntegerT0; uint8_t bClockStop; } PACKED abProtocolDataStructure_T0_t; typedef struct { uint8_t bmFindexDindex; uint8_t bmTCCKST1; uint8_t bGuardTimeT1; uint8_t bWaitingIntegersT1; uint8_t bClockStop; uint8_t bIFSC; uint8_t bNadValue; } PACKED abProtocolDataStructure_T1_t; typedef struct { uint8_t bTimeOut; uint8_t bmFormatString; uint8_t bmPINBlockString; uint8_t bmPINLengthFormat; uint16_t wPINMaxExtraDigit; uint8_t bEntryValidationCondition; uint8_t bNumberMessage; uint16_t wLangId; uint8_t bMsgIndex; uint8_t bTeoPrologue1; uint16_t bTeoPrologue2; } PACKED abPINDataStucture_Verification_t; typedef struct { uint8_t bTimeOut; uint8_t bmFormatString; uint8_t bmPINBlockString; uint8_t bmPINLengthFormat; uint8_t bInsertionOffsetOld; uint8_t bInsertionOffsetNew; uint16_t wPINMaxExtraDigit; uint8_t bConfirmPIN; uint8_t bEntryValidationCondition; uint8_t bNumberMessage; uint16_t wLangId; uint8_t bMsgIndex1; } PACKED abPINDataStucture_Modification_t; typedef struct { uint8_t bMessageType; uint32_t dwLength; uint8_t bSlot; uint8_t bSeq; uint8_t bBWI; uint16_t wLevelParameter; } PACKED PC_to_RDR_XfrBlock_t; typedef struct { uint8_t bMessageType; uint32_t dwLength; uint8_t bSlot; uint8_t bSeq; uint8_t abRFU1; uint16_t abRFU2; } PACKED PC_to_RDR_IccPowerOff_t; typedef struct { uint8_t bMessageType; uint32_t dwLength; uint8_t bSlot; uint8_t bSeq; uint8_t abRFU1; uint16_t abRFU2; } PACKED PC_to_RDR_GetSlotStatus_t; typedef struct { uint8_t bMessageType; uint32_t dwLength; uint8_t bSlot; uint8_t bSeq; uint8_t abRFU1; uint16_t abRFU2; } PACKED PC_to_RDR_GetParameters_t; typedef struct { uint8_t bMessageType; uint32_t dwLength; uint8_t bSlot; uint8_t bSeq; uint8_t abRFU1; uint16_t abRFU2; } PACKED PC_to_RDR_ResetParameters_t; typedef struct { uint8_t bMessageType; uint32_t dwLength; uint8_t bSlot; uint8_t bSeq; uint8_t bProtocolNum; uint16_t abRFU; } PACKED PC_to_RDR_SetParameters_t; typedef struct { uint8_t bMessageType; uint32_t dwLength; uint8_t bSlot; uint8_t bSeq; uint8_t bBWI; uint16_t wLevelParameter; } PACKED PC_to_RDR_Secure_t; typedef struct { uint8_t bMessageType; uint32_t dwLength; uint8_t bSlot; uint8_t bSeq; uint8_t bPowerSelect; uint16_t abRFU; } PACKED PC_to_RDR_IccPowerOn_t; typedef struct { uint8_t bMessageType; uint32_t dwLength; uint8_t bSlot; uint8_t bSeq; uint8_t bStatus; uint8_t bError; uint8_t bClockStatus; } PACKED RDR_to_PC_SlotStatus_t; typedef struct { uint8_t bMessageType; uint32_t dwLength; uint8_t bSlot; uint8_t bSeq; uint8_t bStatus; uint8_t bError; uint8_t bChainParameter; } PACKED RDR_to_PC_DataBlock_t; typedef struct { uint8_t bMessageType; uint32_t dwLength; uint8_t bSlot; uint8_t bSeq; uint8_t bStatus; uint8_t bError; uint8_t bProtocolNum; } PACKED RDR_to_PC_Parameters_t; typedef struct { uint8_t bMessageType; uint8_t bmSlotICCState; /* we support 1 slots, so we need 2*1 bits = 1 byte */ } PACKED RDR_to_PC_NotifySlotChange_t; #ifdef _MSC_VER #undef PACKED #pragma pack(pop) #elif defined(__GNUC__) #undef PACKED #endif #ifdef __cplusplus } #endif #endif OpenSC-0.26.1/src/libopensc/compression.c000066400000000000000000000154701474147347300202260ustar00rootroot00000000000000/* * compression.c: Generic wrapper for compression of data * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef ENABLE_ZLIB /* empty file without zlib */ #include #include #include #include "internal.h" #include "errors.h" #include "compression.h" static int zerr_to_opensc(int err) { switch(err) { case Z_OK: case Z_STREAM_END: return SC_SUCCESS; case Z_UNKNOWN: return SC_ERROR_UNKNOWN; 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_STREAM_ERROR: /* case Z_NEED_DICT: */ default: return SC_ERROR_INTERNAL; } } static int detect_method(const u8* in, size_t inLen) { 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) { /* Since compress does not offer a way to make it compress gzip... manually set it up */ z_stream gz; int err; int window_size = 15 + 0x10; memset(&gz, 0, sizeof(gz)); gz.next_in = (u8*)in; gz.avail_in = (unsigned)inLen; gz.next_out = out; 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); err = deflate(&gz, Z_FINISH); if(err != Z_STREAM_END) { deflateEnd(&gz); return zerr_to_opensc(err); } *outLen = gz.total_out; err = deflateEnd(&gz); return zerr_to_opensc(err); } static int sc_decompress_gzip(u8* out, size_t* outLen, const u8* in, size_t inLen) { /* Since uncompress does not offer a way to make it uncompress gzip... manually set it up */ z_stream gz; int err; int window_size = 15 + 0x20; memset(&gz, 0, sizeof(gz)); gz.next_in = (u8*)in; gz.avail_in = (unsigned)inLen; gz.next_out = out; gz.avail_out = (unsigned)*outLen; *outLen = 0; err = inflateInit2(&gz, window_size); if (err != Z_OK) return zerr_to_opensc(err); err = inflate(&gz, Z_FINISH); if(err != Z_STREAM_END) { inflateEnd(&gz); return zerr_to_opensc(err); } *outLen = gz.total_out; err = inflateEnd(&gz); 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) { unsigned long zlib_outlen; int rc; switch(method) { case COMPRESSION_ZLIB: zlib_outlen = *outLen; rc = zerr_to_opensc(compress(out, &zlib_outlen, in, inLen)); *outLen = zlib_outlen; return rc; case COMPRESSION_GZIP: return sc_compress_gzip(out, outLen, in, inLen); default: return SC_ERROR_INVALID_ARGUMENTS; } } int sc_decompress(u8* out, size_t* outLen, const u8* in, size_t inLen, int method) { unsigned long zlib_outlen; int rc; if (in == NULL || out == NULL) { return SC_ERROR_UNKNOWN_DATA_RECEIVED; } if (method == COMPRESSION_AUTO) { method = detect_method(in, inLen); if (method == COMPRESSION_UNKNOWN) { *outLen = 0; return SC_ERROR_UNKNOWN_DATA_RECEIVED; } } switch (method) { case COMPRESSION_ZLIB: zlib_outlen = *outLen; rc = zerr_to_opensc(uncompress(out, &zlib_outlen, in, inLen)); *outLen = zlib_outlen; return rc; case COMPRESSION_GZIP: return sc_decompress_gzip(out, outLen, in, inLen); default: return SC_ERROR_INVALID_ARGUMENTS; } } static int sc_decompress_zlib_alloc(u8** out, size_t* outLen, const u8* in, size_t inLen, int gzip) { /* Since uncompress does not offer a way to make it uncompress gzip... manually set it up */ z_stream gz; int err; int window_size = 15; 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 = (unsigned)inLen; err = inflateInit2(&gz, window_size); if (err != Z_OK) return zerr_to_opensc(err); *outLen = 0; while (1) { /* Setup buffer... */ size_t num; u8* buf = realloc(*out, bufferSize); if (!buf) { free(*out); *out = NULL; return SC_ERROR_OUT_OF_MEMORY; } *out = buf; gz.next_out = buf + *outLen; gz.avail_out = (unsigned)(bufferSize - *outLen); err = inflate(&gz, Z_FULL_FLUSH); if (err != Z_STREAM_END && err != Z_OK) { free(*out); *out = NULL; break; } num = *outLen + gz.avail_out; if (bufferSize > num) { *outLen += bufferSize - num; bufferSize += bufferSize - num + blockSize; } 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; } } 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 (in == NULL || out == NULL) { return SC_ERROR_UNKNOWN_DATA_RECEIVED; } if (method == COMPRESSION_AUTO) { method = detect_method(in, inLen); if (method == COMPRESSION_UNKNOWN) { return SC_ERROR_UNKNOWN_DATA_RECEIVED; } } switch (method) { case COMPRESSION_ZLIB: return sc_decompress_zlib_alloc(out, outLen, in, inLen, 0); case COMPRESSION_GZIP: return sc_decompress_zlib_alloc(out, outLen, in, inLen, 1); default: return SC_ERROR_INVALID_ARGUMENTS; } } #endif /* ENABLE_ZLIB */ OpenSC-0.26.1/src/libopensc/compression.h000066400000000000000000000025601474147347300202270ustar00rootroot00000000000000/* * compression.h: Generic wrapper for compression of data * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef COMPRESSION_H #define COMPRESSION_H #include "libopensc/opensc.h" #include "libopensc/types.h" #define COMPRESSION_AUTO 0 #define COMPRESSION_ZLIB 1 #define COMPRESSION_GZIP 2 #define COMPRESSION_UNKNOWN (-1) int sc_compress(u8* out, size_t* outLen, const u8* in, size_t inLen, int method); int sc_decompress_alloc(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); #endif OpenSC-0.26.1/src/libopensc/ctbcs.c000066400000000000000000000146371474147347300167670ustar00rootroot00000000000000/* * ctbcs.c: Extended CTBCS commands, used for pcsc and ct-api readers * * 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., 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 "ctbcs.h" static void ctbcs_init_apdu(sc_apdu_t *apdu, int cse, int ins, int p1, int p2) { memset(apdu, 0, sizeof(*apdu)); apdu->cse = cse; apdu->cla = 0x20; apdu->ins = ins; apdu->p1 = p1; apdu->p2 = p2; apdu->control = 1; } static int ctbcs_build_perform_verification_apdu(sc_apdu_t *apdu, struct sc_pin_cmd_data *data) { const char *prompt; size_t buflen, count = 0, j = 0, len; static u8 buf[SC_MAX_APDU_BUFFER_SIZE]; u8 control; ctbcs_init_apdu(apdu, SC_APDU_CASE_3_SHORT, CTBCS_INS_PERFORM_VERIFICATION, CTBCS_P1_INTERFACE1, 0); buflen = sizeof(buf); prompt = data->pin1.prompt; if (prompt && *prompt) { len = strlen(prompt); if (len + 2 > buflen) return SC_ERROR_BUFFER_TOO_SMALL; buf[count++] = CTBCS_TAG_PROMPT; buf[count++] = len; memcpy(buf + count, prompt, len); count += len; } /* card apdu must be last in packet */ if (!data->apdu) return SC_ERROR_INTERNAL; if (count + 12 > buflen) return SC_ERROR_BUFFER_TOO_SMALL; j = count; buf[j++] = CTBCS_TAG_VERIFY_CMD; buf[j++] = 0x00; /* Control byte - length of PIN, and encoding */ control = 0x00; if (data->pin1.encoding == SC_PIN_ENCODING_ASCII) control |= CTBCS_PIN_CONTROL_ENCODE_ASCII; else if (data->pin1.encoding != SC_PIN_ENCODING_BCD) return SC_ERROR_INVALID_ARGUMENTS; if (data->pin1.min_length == data->pin1.max_length) control |= data->pin1.min_length << CTBCS_PIN_CONTROL_LEN_SHIFT; buf[j++] = control; buf[j++] = data->pin1.offset+1; /* Looks like offset is 1-based in CTBCS */ buf[j++] = data->apdu->cla; buf[j++] = data->apdu->ins; buf[j++] = data->apdu->p1; buf[j++] = data->apdu->p2; if (data->flags & SC_PIN_CMD_NEED_PADDING) { len = data->pin1.pad_length; if (2 + j + len > buflen) return SC_ERROR_BUFFER_TOO_SMALL; buf[j++] = len; memset(buf+j, data->pin1.pad_char, len); j += len; } buf[count+1] = j - count - 2; count = j; apdu->lc = apdu->datalen = count; apdu->data = buf; return 0; } static int ctbcs_build_modify_verification_apdu(sc_apdu_t *apdu, struct sc_pin_cmd_data *data) { const char *prompt; size_t buflen, count = 0, j = 0, len; static u8 buf[SC_MAX_APDU_BUFFER_SIZE]; u8 control; ctbcs_init_apdu(apdu, SC_APDU_CASE_3_SHORT, CTBCS_INS_MODIFY_VERIFICATION, CTBCS_P1_INTERFACE1, 0); buflen = sizeof(buf); prompt = data->pin1.prompt; if (prompt && *prompt) { len = strlen(prompt); if (len + 2 > buflen) return SC_ERROR_BUFFER_TOO_SMALL; buf[count++] = CTBCS_TAG_PROMPT; buf[count++] = len; memcpy(buf + count, prompt, len); count += len; } /* card apdu must be last in packet */ if (!data->apdu) return SC_ERROR_INTERNAL; if (count + 12 > buflen) return SC_ERROR_BUFFER_TOO_SMALL; j = count; buf[j++] = CTBCS_TAG_VERIFY_CMD; buf[j++] = 0x00; /* Control byte - length of PIN, and encoding */ control = 0x00; if (data->pin1.encoding == SC_PIN_ENCODING_ASCII) control |= CTBCS_PIN_CONTROL_ENCODE_ASCII; else if (data->pin1.encoding != SC_PIN_ENCODING_BCD) return SC_ERROR_INVALID_ARGUMENTS; if (data->pin1.min_length == data->pin1.max_length) control |= data->pin1.min_length << CTBCS_PIN_CONTROL_LEN_SHIFT; buf[j++] = control; buf[j++] = data->pin1.offset+1; /* Looks like offset is 1-based in CTBCS */ buf[j++] = data->pin2.offset+1; buf[j++] = data->apdu->cla; buf[j++] = data->apdu->ins; buf[j++] = data->apdu->p1; buf[j++] = data->apdu->p2; if (data->flags & SC_PIN_CMD_NEED_PADDING) { len = data->pin1.pad_length + data->pin2.pad_length; if (2 + j + len > buflen) return SC_ERROR_BUFFER_TOO_SMALL; buf[j++] = len; memset(buf+j, data->pin1.pad_char, len); j += len; } buf[count+1] = j - count - 2; count = j; apdu->lc = apdu->datalen = count; apdu->data = buf; return 0; } int ctbcs_pin_cmd(sc_reader_t *reader, struct sc_pin_cmd_data *data) { sc_card_t dummy_card, *card; sc_apdu_t apdu; struct sc_card_operations ops; int r, s; switch (data->cmd) { case SC_PIN_CMD_VERIFY: r = ctbcs_build_perform_verification_apdu(&apdu, data); if (r != SC_SUCCESS) return r; break; case SC_PIN_CMD_CHANGE: case SC_PIN_CMD_UNBLOCK: r = ctbcs_build_modify_verification_apdu(&apdu, data); if (r != SC_SUCCESS) return r; break; default: sc_log(reader->ctx, "Unknown PIN command %d", data->cmd); return SC_ERROR_NOT_SUPPORTED; } memset(&ops, 0, sizeof(ops)); memset(&dummy_card, 0, sizeof(dummy_card)); dummy_card.reader = reader; dummy_card.ctx = reader->ctx; r = sc_mutex_create(reader->ctx, &dummy_card.mutex); if (r != SC_SUCCESS) return r; dummy_card.ops = &ops; card = &dummy_card; r = sc_transmit_apdu(card, &apdu); s = sc_mutex_destroy(reader->ctx, card->mutex); if (s != SC_SUCCESS) { sc_log(reader->ctx, "unable to destroy mutex\n"); return s; } LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); /* Check CTBCS status word */ switch (((unsigned int) apdu.sw1 << 8) | apdu.sw2) { case 0x9000: r = 0; break; case 0x6400: /* Input timed out */ r = SC_ERROR_KEYPAD_TIMEOUT; break; case 0x6401: /* Input cancelled */ r = SC_ERROR_KEYPAD_CANCELLED; break; case 0x6402: /* PINs did not match */ r = SC_ERROR_KEYPAD_PIN_MISMATCH; break; case 0x6700: /* message too long */ r = SC_ERROR_KEYPAD_MSG_TOO_LONG; break; default: r = SC_ERROR_CARD_CMD_FAILED; break; } LOG_TEST_RET(card->ctx, r, "PIN command failed"); /* Calling Function may expect SW1/SW2 in data-apdu set... */ if (data->apdu) { data->apdu->sw1 = apdu.sw1; data->apdu->sw2 = apdu.sw2; } return 0; } OpenSC-0.26.1/src/libopensc/ctbcs.h000066400000000000000000000150341474147347300167640ustar00rootroot00000000000000/* CT-BCS commands, responses and parameters for terminals without keypad and display. This file is part of the Unix driver for Towitoko smart card readers Copyright (C) 1998 1999 2000 Carlos Prados 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 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 _CTBCS_ #define _CTBCS_ /* * Command and response size */ #define CTBCS_MIN_COMMAND_SIZE 2 #define CTBCS_MIN_RESPONSE_SIZE 2 /* * Class byte of all CTBCS commands */ #define CTBCS_CLA 0x20 /* * Mandatory CT-BCS commands */ #define CTBCS_INS_RESET 0x11 /* Reset CT */ #define CTBCS_INS_REQUEST 0x12 /* Request ICC */ #define CTBCS_INS_STATUS 0x13 /* Get reader status */ #define CTBCS_INS_EJECT 0x15 /* Eject ICC */ /* * Additional CT-BCS commands */ #define CTBCS_INS_INPUT 0x16 /* Input from pin pad */ #define CTBCS_INS_OUTPUT 0x17 /* Output to pad pad display */ #define CTBCS_INS_PERFORM_VERIFICATION 0x18 /* Verify PIN from pin pad */ #define CTBCS_INS_MODIFY_VERIFICATION 0x19 /* Perform a change/unblock PIN op */ /* * P1 parameter: functional units */ #define CTBCS_P1_CT_KERNEL 0x00 #define CTBCS_P1_INTERFACE1 0x01 #define CTBCS_P1_INTERFACE2 0x02 #define CTBCS_P1_INTERFACE3 0x03 #define CTBCS_P1_INTERFACE4 0x04 #define CTBCS_P1_INTERFACE5 0x05 #define CTBCS_P1_INTERFACE6 0x06 #define CTBCS_P1_INTERFACE7 0x07 #define CTBCS_P1_INTERFACE8 0x08 #define CTBCS_P1_INTERFACE9 0x09 #define CTBCS_P1_INTERFACE10 0x0A #define CTBCS_P1_INTERFACE11 0x0B #define CTBCS_P1_INTERFACE12 0x0C #define CTBCS_P1_INTERFACE13 0x0D #define CTBCS_P1_INTERFACE14 0x0E #define CTBCS_P1_DISPLAY 0x40 #define CTBCS_P1_KEYPAD 0x50 #define CTBCS_P1_PRINTER 0x60 /* New CT-BCS 1.0 */ #define CTBCS_P1_FINGERPRINT 0x70 /* New CT-BCS 1.0 */ #define CTBCS_P1_VOICEPRINT 0x71 /* New CT-BCS 1.0 */ #define CTBCS_P1_DSV 0x72 /* "Dynamic Signature Verification" New CT-BCS 1.0 */ #define CTBCS_P1_FACE_RECOGNITION 0x73 /* New CT-BCS 1.0 */ #define CTBCS_P1_IRISSCAN 0x74 /* New CT-BCS 1.0 */ /* Other biometric units may use values up to 0x7F */ /* * P2 parameter for Reset CT: data to be returned */ #define CTBCS_P2_RESET_NO_RESP 0x00 /* Return no data */ #define CTBCS_P2_RESET_GET_ATR 0x01 /* Return complete ATR */ #define CTBCS_P2_RESET_GET_HIST 0x02 /* Return historical bytes */ /* * P2 parameter for Request ICC: data to be returned */ #define CTBCS_P2_REQUEST_NO_RESP 0x00 /* Return no data */ #define CTBCS_P2_REQUEST_GET_ATR 0x01 /* Return complete ATR */ #define CTBCS_P2_REQUEST_GET_HIST 0x02 /* Return historical bytes */ /* * P2 parameter for Get status: TAG of data object to return */ #define CTBCS_P2_STATUS_MANUFACTURER 0x46 /* Return manufacturer DO */ #define CTBCS_P2_STATUS_ICC 0x80 /* Return ICC DO */ #define CTBCS_P2_STATUS_TFU 0x81 /* Return Functional Units, new in Version 1.0 */ /* * P2 parameter for Input */ #define CTBCS_P2_INPUT_ECHO 0x01 /* Echo input on display */ #define CTBCS_P2_INPUT_ASTERISKS 0x02 /* Echo input as asterisks */ /* * Tags for parameters to input, output et al. */ #define CTBCS_TAG_PROMPT 0x50 #define CTBCS_TAG_VERIFY_CMD 0x52 #define CTBCS_TAG_TIMEOUT 0x80 /* * PIN command control flags */ #define CTBCS_PIN_CONTROL_LEN_SHIFT 4 #define CTBCS_PIN_CONTROL_LEN_MASK 0x0F #define CTBCS_PIN_CONTROL_ENCODE_ASCII 0x01 /* * General return codes */ #define CTBCS_SW1_OK 0x90 /* Command successful */ #define CTBCS_SW2_OK 0x00 #define CTBCS_SW1_WRONG_LENGTH 0x67 /* Wrong length */ #define CTBCS_SW2_WRONG_LENGTH 0x00 #define CTBCS_SW1_COMMAND_NOT_ALLOWED 0x69 /* Command not allowed */ #define CTBCS_SW2_COMMAND_NOT_ALLOWED 0x00 #define CTBCS_SW1_WRONG_PARAM 0x6A /* Wrong parameters P1, P2 */ #define CTBCS_SW2_WRONG_PARAM 0x00 #define CTBCS_SW1_WRONG_INS 0x6D /* Wrong Instruction */ #define CTBCS_SW2_WRONG_INS 0x00 #define CTBCS_SW1_WRONG_CLA 0x6E /* Class not supported */ #define CTBCS_SW2_WRONG_CLA 0x00 #define CTBCS_SW1_ICC_ERROR 0x6F /* ICC removed, defective or */ #define CTBCS_SW2_ICC_ERROR 0x00 /* no longer reacts */ /* * Return codes for Reset CT */ #define CTBCS_SW1_RESET_CT_OK 0x90 /* Reset CT successful */ #define CTBCS_SW2_RESET_CT_OK 0x00 #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 /* 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 /* * Return codes for Request 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 /* 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 #define CTBCS_SW1_REQUEST_CARD_PRESENT 0x62 /* Card already present */ #define CTBCS_SW2_REQUEST_CARD_PRESENT 0x01 #define CTBCS_SW1_REQUEST_ERROR 0x64 /* Reset not successful */ #define CTBCS_SW2_REQUEST_ERROR 0x00 #define CTBCS_SW1_REQUEST_TIMER_ERROR 0x69 /* Timer not supported */ #define CTBCS_SW2_REQUEST_TIMER_ERROR 0x00 /* * Return codes for Eject ICC */ #define CTBCS_SW1_EJECT_OK 0x90 /* Command successful, */ #define CTBCS_SW2_EJECT_OK 0x00 #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 /* * Data returned on Get Status command */ #define CTBCS_DATA_STATUS_NOCARD 0x00 /* No card present */ #define CTBCS_DATA_STATUS_CARD 0x01 /* Card present */ #define CTBCS_DATA_STATUS_CARD_CONNECT 0x05 /* Card present */ /* * Functions for building CTBCS commands */ int ctbcs_pin_cmd(struct sc_reader *, struct sc_pin_cmd_data *); #endif /* _CTBCS_ */ OpenSC-0.26.1/src/libopensc/ctx.c000066400000000000000000001011111474147347300164470ustar00rootroot00000000000000/* * ctx.c: Context related functions * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #ifdef _WIN32 #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 || ignored_reader(ctx, reader)) { return SC_ERROR_INVALID_ARGUMENTS; } reader->ctx = ctx; list_append(&ctx->readers, reader); return SC_SUCCESS; } int _sc_delete_reader(sc_context_t *ctx, sc_reader_t *reader) { if (reader == NULL) { return SC_ERROR_INVALID_ARGUMENTS; } if (reader->ops->release) reader->ops->release(reader); free(reader->name); free(reader->vendor); list_delete(&ctx->readers, reader); free(reader); return SC_SUCCESS; } struct _sc_driver_entry { const char *name; 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 }, { "gemsafeV1", (void *(*)(void)) sc_get_gemsafeV1_driver }, { "starcos", (void *(*)(void)) sc_get_starcos_driver }, { "tcos", (void *(*)(void)) sc_get_tcos_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 }, #ifdef ENABLE_OPENSSL { "entersafe",(void *(*)(void)) sc_get_entersafe_driver }, #ifdef ENABLE_SM { "epass2003",(void *(*)(void)) sc_get_epass2003_driver }, #endif #endif { "rutoken", (void *(*)(void)) sc_get_rutoken_driver }, { "rutoken_ecp",(void *(*)(void)) sc_get_rtecp_driver }, { "myeid", (void *(*)(void)) sc_get_myeid_driver }, #if defined(ENABLE_OPENSSL) && defined(ENABLE_SM) { "dnie", (void *(*)(void)) sc_get_dnie_driver }, #endif { "masktech", (void *(*)(void)) sc_get_masktech_driver }, { "esteid2018", (void *(*)(void)) sc_get_esteid2018_driver }, { "idprime", (void *(*)(void)) sc_get_idprime_driver }, #if defined(ENABLE_SM) && defined(ENABLE_OPENPACE) { "edo", (void *(*)(void)) sc_get_edo_driver }, #endif /* Here should be placed drivers that need some APDU transactions in the * driver's `match_card()` function. */ { "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 }, { "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 }, { "isoApplet", (void *(*)(void)) sc_get_isoApplet_driver }, #ifdef ENABLE_ZLIB { "gids", (void *(*)(void)) sc_get_gids_driver }, #endif { "openpgp", (void *(*)(void)) sc_get_openpgp_driver }, { "jpki", (void *(*)(void)) sc_get_jpki_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; }; int 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 long rc; HKEY hKey; if (!out || !out_len) return SC_ERROR_INVALID_ARGUMENTS; if (name_env) { 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"; rc = RegOpenKeyExA(HKEY_CURRENT_USER, 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; } } 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; } } return SC_ERROR_OBJECT_NOT_FOUND; #else return SC_ERROR_NOT_SUPPORTED; #endif } /* Simclist helper to locate readers by name */ static int reader_list_seeker(const void *el, const void *key) { const struct sc_reader *reader = (struct sc_reader *)el; if ((el == NULL) || (key == NULL)) return 0; if (strcmp(reader->name, (char*)key) == 0) return 1; return 0; } static void del_drvs(struct _sc_ctx_options *opts) { struct _sc_driver_entry *lst; int *cp, i; lst = opts->cdrv; cp = &opts->ccount; for (i = 0; i < *cp; i++) { free((void *)lst[i].name); } *cp = 0; } static void add_drv(struct _sc_ctx_options *opts, const char *name) { struct _sc_driver_entry *lst; int *cp, max, i; lst = opts->cdrv; cp = &opts->ccount; max = SC_MAX_CARD_DRIVERS; if (*cp == max) /* No space for more drivers... */ return; for (i = 0; i < *cp; i++) if (strcmp(name, lst[i].name) == 0) return; lst[*cp].name = strdup(name); *cp = *cp + 1; } static void add_internal_drvs(struct _sc_ctx_options *opts) { const struct _sc_driver_entry *lst; int i; lst = internal_card_drivers; i = 0; while (lst[i].name != NULL) { add_drv(opts, lst[i].name); i++; } } 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; if (ctx->debug_file && (ctx->debug_file != stderr && ctx->debug_file != stdout)) 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); } /* In Windows, file handles can not be shared between DLL-s, * each DLL has a separate file handle table. Thus tools and utilities * can not set the file handle themselves when -v is specified on command line. */ int sc_ctx_log_to_file(sc_context_t *ctx, const char* filename) { /* Close any existing handles */ if (ctx->debug_file && (ctx->debug_file != stderr && ctx->debug_file != stdout)) { fclose(ctx->debug_file); ctx->debug_file = NULL; } if (!ctx->debug_filename) { if (!filename) filename = "stderr"; ctx->debug_filename = strdup(filename); } if (!filename) return SC_SUCCESS; /* Handle special names */ if (!strcmp(filename, "stdout")) ctx->debug_file = stdout; else if (!strcmp(filename, "stderr")) ctx->debug_file = stderr; else { ctx->debug_file = fopen(filename, "a"); if (ctx->debug_file == NULL) return SC_ERROR_INTERNAL; } 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; int debug; const char *disable_hw_pkcs1_padding; #ifdef _WIN32 char expanded_val[PATH_MAX]; DWORD expanded_len; #endif debug = scconf_get_int(block, "debug", ctx->debug); if (debug > ctx->debug) ctx->debug = debug; val = scconf_get_str(block, "debug_file", NULL); if (val) { #ifdef _WIN32 expanded_len = PATH_MAX; expanded_len = ExpandEnvironmentStringsA(val, expanded_val, expanded_len); if (0 < expanded_len && expanded_len < sizeof expanded_val) val = expanded_val; #endif sc_ctx_log_to_file(ctx, val); } else if (ctx->debug) { sc_ctx_log_to_file(ctx, NULL); } 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; 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; } return err; } /** * find library module for provided driver in configuration file * if not found assume library name equals to module name */ static const char *find_library(sc_context_t *ctx, const char *name) { int i, log_warning; const char *libname = NULL; scconf_block **blocks, *blk; for (i = 0; ctx->conf_blocks[i]; i++) { blocks = scconf_find_blocks(ctx->conf, ctx->conf_blocks[i], "card_driver", name); if (!blocks) continue; blk = blocks[0]; free(blocks); if (blk == NULL) continue; libname = scconf_get_str(blk, "module", name); #ifdef _WIN32 log_warning = libname && libname[0] != '\\'; #else log_warning = libname && libname[0] != '/'; #endif if (log_warning) sc_log(ctx, "warning: relative path to driver '%s' used", libname); break; } return libname; } /** * load card/reader driver modules * Every module should contain a function " void * sc_module_init(char *) " * that returns a pointer to the function _sc_get_xxxx_driver() * used to initialize static modules * Also, an exported "char *sc_module_version" variable should exist in module */ static void *load_dynamic_driver(sc_context_t *ctx, void **dll, const char *name) { const char *version, *libname; void *handle; void *(*modinit)(const char *) = NULL; void *(**tmodi)(const char *) = &modinit; const char *(*modversion)(void) = NULL; const char *(**tmodv)(void) = &modversion; 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; } libname = find_library(ctx, name); if (libname == NULL) return NULL; handle = sc_dlopen(libname); if (handle == NULL) { sc_log(ctx, "Module %s: cannot load %s library: %s", name, libname, sc_dlerror()); return NULL; } /* verify correctness of module */ *(void **)tmodi = sc_dlsym(handle, "sc_module_init"); *(void **)tmodv = sc_dlsym(handle, "sc_driver_version"); if (modinit == NULL || modversion == NULL) { sc_log(ctx, "dynamic library '%s' is not a OpenSC module",libname); sc_dlclose(handle); return NULL; } /* verify module version */ version = modversion(); /* XXX: We really need to have ABI version for each interface */ if (version == NULL || strncmp(version, PACKAGE_VERSION, strlen(PACKAGE_VERSION)) != 0) { sc_log(ctx, "dynamic library '%s': invalid module version", libname); sc_dlclose(handle); return NULL; } *dll = handle; sc_log(ctx, "successfully loaded card driver '%s'", name); return modinit(name); } static int load_card_driver_options(sc_context_t *ctx, struct sc_card_driver *driver) { scconf_block **blocks, *blk; int i; for (i = 0; ctx->conf_blocks[i]; i++) { blocks = scconf_find_blocks(ctx->conf, ctx->conf_blocks[i], "card_driver", driver->short_name); if (!blocks) continue; blk = blocks[0]; free(blocks); if (blk == NULL) continue; /* no options at the moment */ } return SC_SUCCESS; } static int load_card_drivers(sc_context_t *ctx, struct _sc_ctx_options *opts) { const struct _sc_driver_entry *ent; int drv_count; int i; for (drv_count = 0; ctx->card_drivers[drv_count] != NULL; drv_count++) ; for (i = 0; i < opts->ccount; i++) { struct sc_card_driver *(*func)(void) = NULL; struct sc_card_driver *(**tfunc)(void) = &func; void *dll = NULL; int j; if (drv_count >= SC_MAX_CARD_DRIVERS - 1) { sc_log(ctx, "Not more then %i card drivers allowed.", SC_MAX_CARD_DRIVERS); break; } ent = &opts->cdrv[i]; 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); /* if still null, assume driver not found */ if (func == NULL) { sc_log(ctx, "Unable to load '%s'.", ent->name); if (dll) sc_dlclose(dll); continue; } ctx->card_drivers[drv_count] = func(); if (ctx->card_drivers[drv_count] == NULL) { sc_log(ctx, "Driver '%s' not available.", ent->name); continue; } ctx->card_drivers[drv_count]->dll = dll; ctx->card_drivers[drv_count]->atr_map = NULL; ctx->card_drivers[drv_count]->natrs = 0; load_card_driver_options(ctx, ctx->card_drivers[drv_count]); /* Ensure that the list is always terminated by NULL */ ctx->card_drivers[drv_count + 1] = NULL; drv_count++; } return SC_SUCCESS; } static int load_card_atrs(sc_context_t *ctx) { struct sc_card_driver *driver; scconf_block **blocks; int i, j, k; for (i = 0; ctx->conf_blocks[i] != NULL; i++) { blocks = scconf_find_blocks(ctx->conf, ctx->conf_blocks[i], "card_atr", NULL); if (!blocks) continue; for (j = 0; blocks[j] != NULL; j++) { scconf_block *b = blocks[j]; char *atr = b->name->data; const scconf_list *list; struct sc_atr_table t; const char *dname; driver = NULL; if (strlen(atr) < 4) continue; /* The interesting part. If there's no card * driver assigned for the ATR, add it to * the default driver. This will reduce the * amount of code required to process things * related to card_atr blocks in situations, * where the code is not exactly related to * card driver settings, but for example * forcing a protocol at the reader driver. */ dname = scconf_get_str(b, "driver", "default"); /* Find the card driver structure according to dname */ for (k = 0; ctx->card_drivers[k] != NULL; k++) { driver = ctx->card_drivers[k]; if (!strcmp(dname, driver->short_name)) break; driver = NULL; } if (!driver) continue; memset(&t, 0, sizeof(struct sc_atr_table)); t.atr = atr; t.atrmask = (char *) scconf_get_str(b, "atrmask", NULL); t.name = (char *) scconf_get_str(b, "name", NULL); t.type = scconf_get_int(b, "type", SC_CARD_TYPE_UNKNOWN); list = scconf_find_list(b, "flags"); while (list != NULL) { unsigned int flags = 0; if (!list->data) { list = list->next; continue; } 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; t.flags |= flags; list = list->next; } t.card_atr = b; _sc_add_atr(ctx, driver, &t); } free(blocks); } return SC_SUCCESS; } static void process_config_file(sc_context_t *ctx, struct _sc_ctx_options *opts) { int i, r, count = 0; scconf_block **blocks; const char *conf_path = NULL; const char *debug = NULL; #ifdef _WIN32 char temp_path[PATH_MAX]; size_t temp_len; #endif /* Takes effect even when no config around */ debug = getenv("OPENSC_DEBUG"); if (debug) ctx->debug = atoi(debug); memset(ctx->conf_blocks, 0, sizeof(ctx->conf_blocks)); #ifdef _WIN32 temp_len = PATH_MAX-1; r = sc_ctx_win32_get_config_value("OPENSC_CONF", "ConfigFile", "Software\\OpenSC Project\\OpenSC", 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"); if (!conf_path) conf_path = OPENSC_CONF_PATH; #endif ctx->conf = scconf_new(conf_path); if (ctx->conf == NULL) return; r = scconf_parse(ctx->conf); #ifdef OPENSC_CONFIG_STRING /* Parse the string if config file didn't exist */ if (r < 0) r = scconf_parse_string(ctx->conf, OPENSC_CONFIG_STRING); #endif if (r < 1) { /* A negative return value means the config file isn't * there, which is not an error. Nevertheless log this * fact. */ if (r < 0) sc_log(ctx, "scconf_parse failed: %s", ctx->conf->errmsg); else sc_log(ctx, "scconf_parse failed: %s", ctx->conf->errmsg); scconf_free(ctx->conf); 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]; free(blocks); if (strcmp(ctx->app_name, "default") != 0) { blocks = scconf_find_blocks(ctx->conf, NULL, "app", "default"); if (blocks && blocks[0]) ctx->conf_blocks[count] = blocks[0]; free(blocks); } /* 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); } int sc_ctx_detect_readers(sc_context_t *ctx) { int r = 0; const struct sc_reader_driver *drv = ctx->reader_driver; sc_mutex_lock(ctx, ctx->mutex); if (drv->ops->detect_readers != NULL) r = drv->ops->detect_readers(ctx); sc_mutex_unlock(ctx, ctx->mutex); return r; } sc_reader_t *sc_ctx_get_reader(sc_context_t *ctx, unsigned int i) { return list_get_at(&ctx->readers, i); } sc_reader_t *sc_ctx_get_reader_by_id(sc_context_t *ctx, unsigned int id) { return list_get_at(&ctx->readers, id); } sc_reader_t *sc_ctx_get_reader_by_name(sc_context_t *ctx, const char * name) { return list_seek(&ctx->readers, name); } unsigned int sc_ctx_get_reader_count(sc_context_t *ctx) { return list_size(&ctx->readers); } int sc_establish_context(sc_context_t **ctx_out, const char *app_name) { sc_context_param_t ctx_param; memset(&ctx_param, 0, sizeof(sc_context_param_t)); ctx_param.ver = 0; ctx_param.app_name = app_name; return sc_context_create(ctx_out, &ctx_param); } /* For multithreaded issues */ int sc_context_repair(sc_context_t **ctx_out) { /* Must already exist */ if ((ctx_out == NULL) || (*ctx_out == NULL) || ((*ctx_out)->app_name == NULL)) return SC_ERROR_INVALID_ARGUMENTS; /* The only thing that should be shared across different contexts are the * card drivers - so rebuild the ATR's */ load_card_atrs(*ctx_out); /* TODO: May need to re-open any card driver DLL's */ 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; } ctx->ossl3ctx->defprov = OSSL_PROVIDER_load(ctx->ossl3ctx->libctx, "default"); if (ctx->ossl3ctx->defprov == NULL) { OSSL_LIB_CTX_free(ctx->ossl3ctx->libctx); free(ctx->ossl3ctx); ctx->ossl3ctx = NULL; return SC_ERROR_INTERNAL; } 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; ctx = calloc(1, sizeof(sc_context_t)); if (ctx == NULL) return SC_ERROR_OUT_OF_MEMORY; memset(&opts, 0, sizeof(opts)); /* set the application name if set in the parameter options */ if (parm->app_name != NULL) ctx->app_name = strdup(parm->app_name); else ctx->app_name = strdup("default"); if (ctx->app_name == NULL) { sc_release_context(ctx); 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); /* set thread context and create mutex object (if specified) */ if (parm->thread_ctx != NULL) 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, "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(); #elif defined(ENABLE_CRYPTOTOKENKIT) ctx->reader_driver = sc_get_cryptotokenkit_driver(); #elif defined(ENABLE_CTAPI) ctx->reader_driver = sc_get_ctapi_driver(); #elif defined(ENABLE_OPENCT) ctx->reader_driver = sc_get_openct_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); del_drvs(&opts); sc_ctx_detect_readers(ctx); *ctx_out = ctx; return SC_SUCCESS; } /* 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) { 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); return SC_ERROR_NOT_SUPPORTED; } /* Following two are only implemented with internal PC/SC and don't consume a reader object */ int sc_cancel(sc_context_t *ctx) { LOG_FUNC_CALLED(ctx); if (ctx->reader_driver->ops->cancel != NULL) return ctx->reader_driver->ops->cancel(ctx); return SC_ERROR_NOT_SUPPORTED; } 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) { 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; if (ctx == NULL) { return SC_ERROR_INVALID_ARGUMENTS; } SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); 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); for (i = 0; ctx->card_drivers[i]; i++) { struct sc_card_driver *drv = ctx->card_drivers[i]; if (drv->atr_map) _sc_free_atr(ctx, drv); 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) { int r = sc_mutex_destroy(ctx, ctx->mutex); if (r != SC_SUCCESS) { sc_log(ctx, "unable to destroy mutex"); return r; } } if (ctx->conf != NULL) scconf_free(ctx->conf); if (ctx->debug_file && (ctx->debug_file != stdout && ctx->debug_file != stderr)) fclose(ctx->debug_file); free(ctx->debug_filename); free(ctx->app_name); free(ctx->exe_path); list_destroy(&ctx->readers); sc_mem_clear(ctx, sizeof(*ctx)); free(ctx); return SC_SUCCESS; } int sc_set_card_driver(sc_context_t *ctx, const char *short_name) { int i = 0, match = 0; sc_mutex_lock(ctx, ctx->mutex); if (short_name == NULL) { ctx->forced_driver = NULL; match = 1; } else while (i < SC_MAX_CARD_DRIVERS && ctx->card_drivers[i] != NULL) { struct sc_card_driver *drv = ctx->card_drivers[i]; if (strcmp(short_name, drv->short_name) == 0) { ctx->forced_driver = drv; match = 1; break; } i++; } sc_mutex_unlock(ctx, ctx->mutex); if (match == 0) return SC_ERROR_OBJECT_NOT_FOUND; /* FIXME: invent error */ return SC_SUCCESS; } int sc_get_cache_dir(sc_context_t *ctx, char *buf, size_t bufsize) { char *homedir; const char *cache_dir; scconf_block *conf_block = NULL; #ifdef _WIN32 char temp_path[PATH_MAX]; #endif 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) { strlcpy(buf, cache_dir, bufsize); return SC_SUCCESS; } #ifndef _WIN32 #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"; homedir = getenv("USERPROFILE"); /* If USERPROFILE isn't defined, assume it's a single-user OS * and put the cache dir in the Windows dir (usually C:\\WINDOWS) */ if (homedir == NULL || homedir[0] == '\0') { GetWindowsDirectoryA(temp_path, sizeof(temp_path)); homedir = temp_path; } #endif 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; return SC_SUCCESS; } int sc_make_cache_dir(sc_context_t *ctx) { char dirname[PATH_MAX], *sp; int r, mkdir_checker; size_t j, namelen; if ((r = sc_get_cache_dir(ctx, dirname, sizeof(dirname))) < 0) return r; namelen = strlen(dirname); while (1) { #ifdef _WIN32 mkdir_checker = mkdir(dirname) >= 0; #else mkdir_checker = mkdir(dirname, 0700) >= 0; #endif if (mkdir_checker) break; if (errno != ENOENT || (sp = strrchr(dirname, '/')) == NULL || sp == dirname) goto failed; *sp = '\0'; } /* We may have stripped one or more path components from * the directory name. Restore them */ while (1) { j = strlen(dirname); if (j >= namelen) break; dirname[j] = '/'; #ifdef _WIN32 mkdir_checker = mkdir(dirname) < 0; #else mkdir_checker = mkdir(dirname, 0700) < 0; #endif if (mkdir_checker) goto failed; } return SC_SUCCESS; /* for lack of a better return code */ failed: sc_log(ctx, "failed to create cache directory"); return SC_ERROR_INTERNAL; } OpenSC-0.26.1/src/libopensc/cwa-dnie.c000066400000000000000000001517271474147347300173620ustar00rootroot00000000000000/** * cwa-dnie.c: DNIe data provider for CWA SM handling. * * 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 * Direccion General de la Policia y de la Guardia Civil * * 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 */ #define __SM_DNIE_C__ #ifdef HAVE_CONFIG_H #include "config.h" #endif #if defined(ENABLE_OPENSSL) && defined(ENABLE_SM) /* empty file without openssl or sm */ #include #include #include "opensc.h" #include "cardctl.h" #include "internal.h" #include "cwa14890.h" #include "cwa-dnie.h" #include #include #include #include #include #if OPENSSL_VERSION_NUMBER >= 0x30000000L # include # include #endif #define MAX_RESP_BUFFER_SIZE 2048 /********************* Keys and certificates as published by DGP ********/ /** * Public Key modulus for the ROOT CA for DNIe (pk-RCAicc->n) */ 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, 0x0D, 0xAF, 0x24, 0x58, 0x02, 0x82, 0x88, 0xF5, 0x4C, 0xE5, 0x52, 0xF8, 0xFA, 0x57, 0xAB, 0x2F, 0xB1, 0x03, 0xB1, 0x12, 0x42, 0x7E, 0x11, 0x13, 0x1D, 0x1D, 0x27, 0xE1, 0x0A, 0x5B, 0x50, 0x0E, 0xAA, 0xE5, 0xD9, 0x40, 0x30, 0x1E, 0x30, 0xEB, 0x26, 0xC3, 0xE9, 0x06, 0x6B, 0x25, 0x71, 0x56, 0xED, 0x63, 0x9D, 0x70, 0xCC, 0xC0, 0x90, 0xB8, 0x63, 0xAF, 0xBB, 0x3B, 0xFE, 0xD8, 0xC1, 0x7B, 0xE7, 0x67, 0x30, 0x34, 0xB9, 0x82, 0x3E, 0x97, 0x7E, 0xD6, 0x57, 0x25, 0x29, 0x27, 0xF9, 0x57, 0x5B, 0x9F, 0xFF, 0x66, 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 (pk-RCAicc->e) */ static u8 icc_root_ca_public_exponent[] = { 0x01, 0x00, 0x01 }; /** * Terminal (IFD) key modulus for SM channel creation (dnieRealParam->sk-IFD-AUT->n) */ 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, 0xcc, 0x1f, 0x92, 0x49, 0x35, 0x85, 0xe1, 0x34, 0xa1, 0xb4, 0x2d, 0xe5, 0x19, 0xf4, 0x63, 0x24, 0x4d, 0x7e, 0xd3, 0x84, 0xe2, 0x6d, 0x51, 0x6c, 0xc7, 0xa4, 0xff, 0x78, 0x95, 0xb1, 0x99, 0x21, 0x40, 0x04, 0x3a, 0xac, 0xad, 0xfc, 0x12, 0xe8, 0x56, 0xb2, 0x02, 0x34, 0x6a, 0xf8, 0x22, 0x6b, 0x1a, 0x88, 0x21, 0x37, 0xdc, 0x3c, 0x5a, 0x57, 0xf0, 0xd2, 0x81, 0x5c, 0x1f, 0xcd, 0x4b, 0xb4, 0x6f, 0xa9, 0x15, 0x7f, 0xdf, 0xfd, 0x79, 0xec, 0x3a, 0x10, 0xa8, 0x24, 0xcc, 0xc1, 0xeb, 0x3c, 0xe0, 0xb6, 0xb4, 0x39, 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 (dnie30RealParamPIN->sk-IFD-AUT->n) */ 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, 0x71, 0xA9, 0xD1, 0x30, 0x1D, 0x3F, 0xB2, 0x8F, 0x38, 0xC5, 0x7D, 0x08, 0x89, 0xF7, 0x31, 0xDB, 0x8E, 0xDD, 0xBC, 0x13, 0x67, 0xC1, 0x34, 0xE1, 0xE9, 0x47, 0x78, 0x6B, 0x8E, 0xC8, 0xE4, 0xB9, 0xCA, 0x6A, 0xA7, 0xC2, 0x4C, 0x86, 0x91, 0xC7, 0xBE, 0x2F, 0xD8, 0xC1, 0x23, 0x66, 0x0E, 0x98, 0x65, 0xE1, 0x4F, 0x19, 0xDF, 0xFB, 0xB7, 0xFF, 0x38, 0x08, 0xC9, 0xF2, 0x04, 0xE7, 0x97, 0xD0, 0x6D, 0xD8, 0x33, 0x3A, 0xC5, 0x83, 0x86, 0xEE, 0x4E, 0xB6, 0x1E, 0x20, 0xEC, 0xA7, 0xEF, 0x38, 0xD5, 0xB0, 0x5E, 0xB1, 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 */ static u8 ifd_public_exponent[] = { 0x01, 0x00, 0x01 }; /** * Terminal (IFD) public exponent for SM channel creation for PIN channel DNIe 3.0 */ static u8 ifd_pin_public_exponent[] = { 0x01, 0x00, 0x01 }; /** * Terminal (IFD) private exponent for SM channel establishment (dnieRealParam->sk-IFD-AUT->d) */ 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, 0x91, 0x65, 0x81, 0x9c, 0xa4, 0xde, 0xc9, 0x4a, 0xa0, 0x6b, 0x69, 0xbc, 0xe8, 0x17, 0x06, 0xd1, 0xc1, 0xb6, 0x68, 0xeb, 0x12, 0x86, 0x95, 0xe5, 0xf7, 0xfe, 0xde, 0x18, 0xa9, 0x08, 0xa3, 0x01, 0x1a, 0x64, 0x6a, 0x48, 0x1d, 0x3e, 0xa7, 0x1d, 0x8a, 0x38, 0x7d, 0x47, 0x46, 0x09, 0xbd, 0x57, 0xa8, 0x82, 0xb1, 0x82, 0xe0, 0x47, 0xde, 0x80, 0xe0, 0x4b, 0x42, 0x21, 0x41, 0x6b, 0xd3, 0x9d, 0xfa, 0x1f, 0xac, 0x03, 0x00, 0x64, 0x19, 0x62, 0xad, 0xb1, 0x09, 0xe2, 0x8c, 0xaf, 0x50, 0x06, 0x1b, 0x68, 0xc9, 0xca, 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 (dnie30RealParamDataPIN->sk-IFD-AUT->d) */ 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, 0xFC, 0x43, 0x89, 0xEB, 0x71, 0x7B, 0x85, 0x02, 0x04, 0xCD, 0xF3, 0x30, 0xD6, 0x46, 0xFC, 0x7B, 0x2B, 0x19, 0x29, 0xD6, 0x8C, 0xBE, 0x39, 0x49, 0x7B, 0x62, 0x3A, 0x82, 0xC7, 0x64, 0x1A, 0xC3, 0x48, 0x79, 0x57, 0x3D, 0xEA, 0x0D, 0xAB, 0xC7, 0xCA, 0x30, 0x9A, 0xE4, 0xB3, 0xED, 0xDA, 0xFA, 0xEE, 0x55, 0xD5, 0x42, 0xF7, 0x80, 0x23, 0x03, 0x51, 0xE7, 0x5E, 0x7F, 0x32, 0xDC, 0x65, 0x2E, 0xF1, 0xED, 0x47, 0xA5, 0x1C, 0x18, 0xD9, 0xDF, 0x9F, 0xF4, 0x8D, 0x87, 0x8D, 0xB6, 0x22, 0xEA, 0x6E, 0x93, 0x70, 0xE9, 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) (c-CV-CA-CS-AUT) */ 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, 0x1a, 0xed, 0xfa, 0xb2, 0xbf, 0xac, 0x90, 0x92, 0xfc, 0x70, 0xcc, 0xc0, 0x0c, 0xaf, 0x48, 0x2a, 0x4b, 0xe3, 0x1a, 0xfd, 0xbd, 0x3c, 0xbc, 0x8c, 0x83, 0x82, 0xcf, 0x06, 0xbc, 0x07, 0x19, 0xba, 0xab, 0xb5, 0x6b, 0x6e, 0xc8, 0x07, 0x60, 0xa4, 0xa9, 0x3f, 0xa2, 0xd7, 0xc3, 0x47, 0xf3, 0x44, 0x27, 0xf9, 0xff, 0x5c, 0x8d, 0xe6, 0xd6, 0x5d, 0xac, 0x95, 0xf2, 0xf1, 0x9d, 0xac, 0x00, 0x53, 0xdf, 0x11, 0xa5, 0x07, 0xfb, 0x62, 0x5e, 0xeb, 0x8d, 0xa4, 0xc0, 0x29, 0x9e, 0x4a, 0x21, 0x12, 0xab, 0x70, 0x47, 0x58, 0x8b, 0x8d, 0x6d, 0xa7, 0x59, 0x22, 0x14, 0xf2, 0xdb, 0xa1, 0x40, 0xc7, 0xd1, 0x22, 0x57, 0x9b, 0x5f, 0x38, 0x3d, 0x22, 0x53, 0xc8, 0xb9, 0xcb, 0x5b, 0xc3, 0x54, 0x3a, 0x55, 0x66, 0x0b, 0xda, 0x80, 0x94, 0x6a, 0xfb, 0x05, 0x25, 0xe8, 0xe5, 0x58, 0x6b, 0x4e, 0x63, 0xe8, 0x92, 0x41, 0x49, 0x78, 0x36, 0xd8, 0xd3, 0xab, 0x08, 0x8c, 0xd4, 0x4c, 0x21, 0x4d, 0x6a, 0xc8, 0x56, 0xe2, 0xa0, 0x07, 0xf4, 0x4f, 0x83, 0x74, 0x33, 0x37, 0x37, 0x1a, 0xdd, 0x8e, 0x03, 0x00, 0x01, 0x00, 0x01, 0x42, 0x08, 0x65, 0x73, 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) (dnieRealParamData->c-CV-IFD-AUT) */ 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, 0xe8, 0x30, 0xe4, 0x25, 0x57, 0x2d, 0xe8, 0x28, 0xfa, 0xf4, 0xde, 0x1b, 0x01, 0xc3, 0x94, 0xe3, 0x45, 0xc2, 0xfb, 0x06, 0x29, 0xa3, 0x93, 0x49, 0x2f, 0x94, 0xf5, 0x70, 0xb0, 0x0b, 0x1d, 0x67, 0x77, 0x29, 0xf7, 0x55, 0xd1, 0x07, 0x02, 0x2b, 0xb0, 0xa1, 0x16, 0xe1, 0xd7, 0xd7, 0x65, 0x9d, 0xb5, 0xc4, 0xac, 0x0d, 0xde, 0xab, 0x07, 0xff, 0x04, 0x5f, 0x37, 0xb5, 0xda, 0xf1, 0x73, 0x2b, 0x54, 0xea, 0xb2, 0x38, 0xa2, 0xce, 0x17, 0xc9, 0x79, 0x41, 0x87, 0x75, 0x9c, 0xea, 0x9f, 0x92, 0xa1, 0x78, 0x05, 0xa2, 0x7c, 0x10, 0x15, 0xec, 0x56, 0xcc, 0x7e, 0x47, 0x1a, 0x48, 0x8e, 0x6f, 0x1b, 0x91, 0xf7, 0xaa, 0x5f, 0x38, 0x3c, 0xad, 0xfc, 0x12, 0xe8, 0x56, 0xb2, 0x02, 0x34, 0x6a, 0xf8, 0x22, 0x6b, 0x1a, 0x88, 0x21, 0x37, 0xdc, 0x3c, 0x5a, 0x57, 0xf0, 0xd2, 0x81, 0x5c, 0x1f, 0xcd, 0x4b, 0xb4, 0x6f, 0xa9, 0x15, 0x7f, 0xdf, 0xfd, 0x79, 0xec, 0x3a, 0x10, 0xa8, 0x24, 0xcc, 0xc1, 0xeb, 0x3c, 0xe0, 0xb6, 0xb4, 0x39, 0x6a, 0xe2, 0x36, 0x59, 0x00, 0x16, 0xba, 0x69, 0x00, 0x01, 0x00, 0x01, 0x42, 0x08, 0x65, 0x73, 0x53, 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 (dnie30RealParamDataPIN->c-CV-IFD-AUT) */ 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, 0xd7, 0x2f, 0xb6, 0xdf, 0x89, 0x9a, 0xf1, 0x5b, 0x71, 0x78, 0x21, 0xbf, 0xb1, 0xaf, 0x7d, 0x75, 0x85, 0x01, 0x6d, 0x8c, 0x36, 0xaf, 0x4a, 0xc2, 0xa0, 0xb0, 0xc5, 0x2a, 0xd6, 0x5b, 0x69, 0x25, 0x67, 0x31, 0xc3, 0x4d, 0x59, 0x02, 0x0e, 0x87, 0xab, 0x73, 0xa2, 0x30, 0xfa, 0x69, 0xee, 0x82, 0xb3, 0x3a, 0x31, 0xdf, 0x04, 0x0c, 0xe9, 0x0f, 0x0a, 0xfc, 0x3a, 0x11, 0x1d, 0x35, 0xda, 0x95, 0x66, 0xa8, 0xcd, 0xab, 0xea, 0x0e, 0x3f, 0x75, 0x94, 0xc4, 0x40, 0xd3, 0x74, 0x50, 0x7a, 0x94, 0x35, 0x57, 0x59, 0xb3, 0x9e, 0xc5, 0xe5, 0xfc, 0xb8, 0x03, 0x8d, 0x79, 0x3d, 0x5f, 0x9b, 0xa8, 0xb5, 0xb1, 0x0b, 0x70, 0x5f, 0x38, 0x3c, 0x4c, 0x86, 0x91, 0xc7, 0xbe, 0x2f, 0xd8, 0xc1, 0x23, 0x66, 0x0e, 0x98, 0x65, 0xe1, 0x4f, 0x19, 0xdf, 0xfb, 0xb7, 0xff, 0x38, 0x08, 0xc9, 0xf2, 0x04, 0xe7, 0x97, 0xd0, 0x6d, 0xd8, 0x33, 0x3a, 0xc5, 0x83, 0x86, 0xee, 0x4e, 0xb6, 0x1e, 0x20, 0xec, 0xa7, 0xef, 0x38, 0xd5, 0xb0, 0x5e, 0xb1, 0x15, 0x96, 0x6a, 0x5a, 0x89, 0xad, 0x58, 0xa5, 0x00, 0x01, 0x00, 0x01, 0x42, 0x08, 0x65, 0x73, 0x53, 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 (pk-RCA-AUT-keyRef) */ static u8 root_ca_keyref[] = { 0x02, 0x0f }; /** * ICC card private key reference (sk-ICC-AUT-keyRef) */ static u8 icc_priv_keyref[] = { 0x02, 0x1f }; /** * Intermediate CA card key reference (ifd-keyRef) */ 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 (dnieRealParamData->pk-IFD-AUT-keyRef) */ 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 (dnie30RealParamDataPIN->pk-IFD-AUT-keyRef) */ 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 (dnieRealParamData->sn-IFD) */ 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 (dnie30RealParamDataPIN->sn-IFD) */ 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 **********************************/ /** * Select a file from card, process fci and read data. * * This is done by mean of iso_select_file() and iso_read_binary() * * @param card pointer to sc_card data * @param path pathfile * @param file pointer to resulting file descriptor * @param buffer pointer to buffer where to store file contents * @param length length of buffer data * @return SC_SUCCESS if ok; else error code */ int dnie_read_file(sc_card_t * card, const sc_path_t * path, sc_file_t ** file, u8 ** buffer, size_t * length) { u8 *data = NULL; char *msg = NULL; int res = SC_SUCCESS; size_t fsize = 0; /* file size */ sc_context_t *ctx = NULL; if (!card || !card->ctx) return SC_ERROR_INVALID_ARGUMENTS; ctx = card->ctx; LOG_FUNC_CALLED(card->ctx); if (!buffer || !length || !path) /* check received arguments */ LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); /* select file by mean of iso7816 ops */ res = card->ops->select_file(card, path, file); if (res != SC_SUCCESS || !file || !(*file)) { msg = "select_file failed"; goto dnie_read_file_err; } /* iso's select file calls if needed process_fci, so arriving here * we have file structure filled. */ if ((*file)->type == SC_FILE_TYPE_DF) { /* just a DF, no need to read_binary() */ *buffer = NULL; *length = 0; res = SC_SUCCESS; msg = "File is a DF: no need to read_binary()"; goto dnie_read_file_end; } fsize = (*file)->size; /* reserve enough space to read data from card */ if (fsize <= 0) { res = SC_ERROR_FILE_TOO_SMALL; msg = "provided buffer size is too small"; goto dnie_read_file_err; } data = calloc(fsize, sizeof(u8)); if (data == NULL) { res = SC_ERROR_OUT_OF_MEMORY; msg = "cannot reserve requested buffer size"; goto dnie_read_file_err; } /* call sc_read_binary() to retrieve data */ 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 read */ res = SC_ERROR_CARD_CMD_FAILED; msg = "read_binary() failed"; goto dnie_read_file_err; } *buffer = data; *length = res; /* arriving here means success */ res = SC_SUCCESS; goto dnie_read_file_end; dnie_read_file_err: if (data) free(data); if (file) { sc_file_free(*file); *file = NULL; } dnie_read_file_end: if (msg) sc_log(ctx, "%s", msg); LOG_FUNC_RETURN(ctx, res); } /** * Read SM required certificates from card. * * This function uses received path to read a certificate file from * 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 resulting data * @return SC_SUCCESS if ok, else error code */ static int dnie_read_certificate(sc_card_t * card, char *certpath, X509 ** cert) { sc_file_t *file = NULL; sc_path_t path; u8 *buffer = NULL, *buffer2 = NULL; char *msg = NULL; size_t bufferlen = 0; int res = SC_SUCCESS; LOG_FUNC_CALLED(card->ctx); sc_format_path(certpath, &path); res = dnie_read_file(card, &path, &file, &buffer, &bufferlen); if (res != SC_SUCCESS) { msg = "Cannot get intermediate CA cert"; goto read_cert_end; } 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; } res = SC_SUCCESS; read_cert_end: if (buffer) { free(buffer); buffer = NULL; bufferlen = 0; } sc_file_free(file); file = NULL; if (msg) sc_log(card->ctx, "%s", msg); 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); 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 **************/ /** * Retrieve Root CA public key. * * Just returns (as local SM authentication) static data * @param card Pointer to card driver structure * @param root_ca_key pointer to resulting returned key * @return SC_SUCCESS if ok; else error code */ static int dnie_get_root_ca_pubkey(sc_card_t * card, EVP_PKEY ** root_ca_key) { 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(); *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; } 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); 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) { 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); } /** * Retrieve IFD (application) CVC intermediate CA certificate and length. * * Returns a byte array with the intermediate CA certificate * (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 always return success * * @param card Pointer to card driver Certificate * @param cert Where to store resulting byte array * @param length len of returned byte array * @return SC_SUCCESS if ok; else error code */ 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); /* 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); } /** * Retrieve IFD (application) CVC certificate and length. * * Returns a byte array with the application's certificate * (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 always return success * * @param card Pointer to card driver Certificate * @param cert Where to store resulting byte array * @param length len of returned byte array * @return SC_SUCCESS if ok; else error code */ 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); /* 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); } /** * Retrieve IFD (application) CVC certificate and length for * the PIN channel. * * Returns a byte array with the application's certificate * (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 always return success * * @param card Pointer to card driver Certificate * @param cert Where to store resulting byte array * @param length len of returned byte array * @return SC_SUCCESS if ok; else error code */ 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); /* 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); } /** * Get IFD (Terminal) private key data passing the three * arguments (modulus, public and private exponent). * * @param card pointer to card driver structure * @param ifd_privkey where to store IFD private key * @param modulus the byte array used as the modulus of the key * @param modulus_len the length of the modulus * @param public_exponent the byte array for the public exponent * @param public_exponent_len the length of the public exponent * @param private_exponent the byte array for the private exponent * @param private_exponent_len the length of the private exponent * @return SC_SUCCESS if ok; else error code */ static int dnie_get_privkey(sc_card_t * card, EVP_PKEY ** ifd_privkey, u8 * modulus, int modulus_len, u8 * public_exponent, size_t public_exponent_len, u8 * private_exponent, size_t private_exponent_len) { BIGNUM *ifd_rsa_n = NULL, *ifd_rsa_e = NULL, *ifd_rsa_d = NULL; #if OPENSSL_VERSION_NUMBER < 0x30000000L int res = SC_ERROR_INTERNAL; RSA *ifd_rsa = NULL; LOG_FUNC_CALLED(card->ctx); ifd_rsa = RSA_new(); *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, (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); RSA_free(ifd_rsa); EVP_PKEY_free(*ifd_privkey); sc_log(card->ctx, "Cannot set RSA values for IFD private key"); return SC_ERROR_INTERNAL; } 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); } /** * Get IFD (Terminal) private key data * * As this is a local (in memory) provider, just get data specified in * DNIe's manual and compose an OpenSSL private key structure * * @param card pointer to card driver structure * @param ifd_privkey where to store IFD private key * @return SC_SUCCESS if ok; else error code */ static int dnie_get_ifd_privkey(sc_card_t * card, EVP_PKEY ** ifd_privkey) { 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); } /** * Get IFD (Terminal) private key data for the PIN channel DNIe 3.0 * * As this is a local (in memory) provider, just get data specified in * DNIe's manual and compose an OpenSSL private key structure * * @param card pointer to card driver structure * @param ifd_privkey where to store IFD private key * @return SC_SUCCESS if ok; else error code */ static int dnie_get_ifd_privkey_pin(sc_card_t * card, EVP_PKEY ** ifd_privkey) { 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); } /** * Get ICC intermediate CA Certificate from card. * * @param card Pointer to card driver structure * @param cert where to store resulting certificate * @return SC_SUCCESS if ok; else error code */ static int dnie_get_icc_intermediate_ca_cert(sc_card_t * card, X509 ** 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; } /** * Get ICC (card) certificate. * * @param card Pointer to card driver structure * @param cert where to store resulting certificate * @return SC_SUCCESS if ok; else error code */ static int dnie_get_icc_cert(sc_card_t * card, X509 ** cert) { return dnie_read_certificate(card, "3F00601F", cert); } /** * Retrieve key reference for Root CA to validate CVC intermediate CA certs. * * This is required in the process of On card external authenticate * @param card Pointer to card driver structure * @param buf where to store resulting key reference * @param len where to store buffer length * @return SC_SUCCESS if ok; else error code */ static int dnie_get_root_ca_pubkey_ref(sc_card_t * card, u8 ** buf, size_t * len) { 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 always returned * * @param card Pointer to card driver structure * @param buf where to store resulting key reference * @param len where to store buffer length * @return SC_SUCCESS if ok; else error code */ static int dnie_get_intermediate_ca_pubkey_ref(sc_card_t * card, u8 ** buf, size_t * len) { 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; } /** * Retrieve public key reference for IFD certificate. * * 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 always returned * * @param card pointer to card driver structure * @param buf where to store data to be sent * @param len where to store data length * @return SC_SUCCESS if ok; else error code */ static int dnie_get_ifd_pubkey_ref(sc_card_t * card, u8 ** buf, size_t * len) { 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; } /** * Retrieve public key reference for IFD certificate for the PIN channel. * * 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 always returned * * @param card pointer to card driver structure * @param buf where to store data to be sent * @param len where to store data length * @return SC_SUCCESS if ok; else error code */ 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); /* 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 establishment, just retrieve key reference from static * data tables and just return success * * @param card pointer to card driver structure * @param buf where to store data * @param len where to store data length * @return SC_SUCCESS if ok; else error */ static int dnie_get_icc_privkey_ref(sc_card_t * card, u8 ** buf, size_t * len) { 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; } /** * Retrieve SN.IFD (8 bytes left padded with zeroes if required). * * In DNIe local SM procedure, just read it from static data and * return SC_SUCCESS * * @param card pointer to card structure * @param buf where to store result (8 bytes) * @return SC_SUCCESS if ok; else error */ 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; /* 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; } /** * Retrieve SN.IFD (8 bytes left padded with zeroes if required) * for the PIN channel DNIe 3.0. * * In DNIe local SM procedure, just read it from static data and * return SC_SUCCESS * * @param card pointer to card structure * @return SC_SUCCESS if ok; else error */ 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; /* 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). * * As DNIe reads serial number at startup, no need to read again * Just retrieve it from cache and return success * * @param card pointer to card structure * @return SC_SUCCESS if ok; else error */ static int dnie_get_sn_icc(sc_card_t * card) { int res=SC_SUCCESS; sc_serial_number_t serial; 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 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); return SC_SUCCESS; } /** * CWA-14890 SM stablisment pre-operations. * * DNIe needs to get icc serial number at the begin of the sm creation * (to avoid breaking key references) so get it an store into serialnr * cache here. * * In this way if get_sn_icc is called(), we make sure that no APDU * command is to be sent to card, just retrieve it from cache * * @param card pointer to card driver structure * @param provider pointer to SM data provider for DNIe * @return SC_SUCCESS if OK. else error code */ static int dnie_create_pre_ops(sc_card_t * card, cwa_provider_t * provider) { sc_serial_number_t serial; /* make sure that this cwa provider is used with a working DNIe card */ if (card->type != SC_CARD_TYPE_DNIE_USER) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_CARD); /* ensure that Card Serial Number is properly cached */ return sc_card_ctl(card, SC_CARDCTL_GET_SERIALNR, &serial); } /** * Main entry point for DNIe CWA14890 SM data provider. * * Return a pointer to DNIe data provider with proper function pointers * * @param card pointer to card driver data structure * @return cwa14890 DNIe data provider if success, null on error */ cwa_provider_t *dnie_get_cwa_provider(sc_card_t * card) { cwa_provider_t *res = cwa_get_default_provider(card); if (!res) return NULL; /* set up proper data */ /* pre and post operations */ res->cwa_create_pre_ops = dnie_create_pre_ops; /* Get ICC intermediate CA path */ res->cwa_get_icc_intermediate_ca_cert = dnie_get_icc_intermediate_ca_cert; /* Get ICC certificate path */ res->cwa_get_icc_cert = dnie_get_icc_cert; /* Obtain RSA public key from RootCA */ res->cwa_get_root_ca_pubkey = dnie_get_root_ca_pubkey; /* Obtain RSA IFD private key */ res->cwa_get_ifd_privkey = dnie_get_ifd_privkey; /* Retrieve CVC intermediate CA certificate and length */ res->cwa_get_cvc_ca_cert = dnie_get_cvc_ca_cert; /* Retrieve CVC IFD certificate and length */ res->cwa_get_cvc_ifd_cert = dnie_get_cvc_ifd_cert; /* Get public key references for Root CA to validate intermediate CA cert */ res->cwa_get_root_ca_pubkey_ref = dnie_get_root_ca_pubkey_ref; /* Get public key reference for IFD intermediate CA certificate */ res->cwa_get_intermediate_ca_pubkey_ref = dnie_get_intermediate_ca_pubkey_ref; /* Get public key reference for IFD CVC certificate */ res->cwa_get_ifd_pubkey_ref = dnie_get_ifd_pubkey_ref; /* Get ICC private key reference */ res->cwa_get_icc_privkey_ref = dnie_get_icc_privkey_ref; /* Get IFD Serial Number */ res->cwa_get_sn_ifd = dnie_get_sn_ifd; /* Get ICC Serial Number */ res->cwa_get_sn_icc = dnie_get_sn_icc; return res; } /** * Changes the provider to use the common secure (DNIe 2.0) * channel. * * @param card the card to change the cwa provider for */ void dnie_change_cwa_provider_to_secure(sc_card_t * card) { cwa_provider_t * res = GET_DNIE_PRIV_DATA(card)->cwa_provider; /* redefine different IFD data for secure channel */ res->cwa_get_cvc_ifd_cert = dnie_get_cvc_ifd_cert; res->cwa_get_ifd_privkey = dnie_get_ifd_privkey; res->cwa_get_ifd_pubkey_ref = dnie_get_ifd_pubkey_ref; res->cwa_get_sn_ifd = dnie_get_sn_ifd; } /** * Changes the provider to use the new PIN (DNIe 3.0) * channel. * * @param card the card to change the cwa provider for */ void dnie_change_cwa_provider_to_pin(sc_card_t * card) { cwa_provider_t * res = GET_DNIE_PRIV_DATA(card)->cwa_provider; /* redefine different IFD data for PIN channel */ res->cwa_get_cvc_ifd_cert = dnie_get_cvc_ifd_cert_pin; res->cwa_get_ifd_privkey = dnie_get_ifd_privkey_pin; res->cwa_get_ifd_pubkey_ref = dnie_get_ifd_pubkey_ref_pin; res->cwa_get_sn_ifd = dnie_get_sn_ifd_pin; } void dnie_format_apdu(sc_card_t *card, sc_apdu_t *apdu, 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) { sc_format_apdu(card, apdu, cse, ins, p1, p2); apdu->le = le; apdu->lc = lc; if (resp != NULL) { apdu->resp = resp; apdu->resplen = resplen; } if (data != NULL) { apdu->data = data; apdu->datalen = datalen; } } #endif /* HAVE_OPENSSL */ /* _ end of cwa-dnie.c - */ OpenSC-0.26.1/src/libopensc/cwa-dnie.h000066400000000000000000000067711474147347300173650ustar00rootroot00000000000000/** * 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 * Direccion General de la Policia y de la Guardia Civil * * 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 __CWADNIE_H__ #define __CWADNIE_H__ #if defined(ENABLE_OPENSSL) && defined(ENABLE_SM) #include "libopensc/opensc.h" #ifdef ENABLE_DNIE_UI /** * To handle user interface routines */ typedef struct ui_context { int user_consent_enabled; char *user_consent_app; } ui_context_t; #endif 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 * * Defines internal data used in OpenDNIe code */ typedef struct dnie_private_data_st { /* sc_serial_number_t *serialnumber; < Cached copy of card serial number NOT USED AT THE MOMENT */ int rsa_key_ref; /**< Key id reference being used in sec operation */ u8 *cache; /**< Cache buffer for read_binary() operation */ size_t cachelen; /**< length of cache buffer */ struct cwa_provider_st *cwa_provider; #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 */ #define GET_DNIE_PRIV_DATA(card) ((dnie_private_data_t *) ((card)->drv_data)) #define GET_DNIE_UI_CTX(card) (((dnie_private_data_t *) ((card)->drv_data))->ui_ctx) #define DNIE_30_VERSION 0x04 #define DNIE_30_CACHE_COUNTER 30000 cwa_provider_t *dnie_get_cwa_provider(sc_card_t * card); 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, size_t le, size_t lc, unsigned char * resp, size_t resplen, const unsigned char * data, size_t datalen); #endif #endif OpenSC-0.26.1/src/libopensc/cwa14890.c000066400000000000000000002006171474147347300170440ustar00rootroot00000000000000/** * 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 * Direccion General de la Policia y de la Guardia Civil * * 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 */ #define __CWA14890_C__ #ifdef HAVE_CONFIG_H #include "config.h" #endif #if defined(ENABLE_OPENSSL) && defined(ENABLE_SM) /* empty file without openssl or sm */ #include #include #include #include "opensc.h" #include "cardctl.h" #include "internal.h" #include #include #include #include #include #include "cwa14890.h" #include "cwa-dnie.h" #if OPENSSL_VERSION_NUMBER >= 0x30000000L # include # include # include #endif #define MAX_RESP_BUFFER_SIZE 2048 /** * Structure used to compose BER-TLV encoded data * according to iso7816-4 sect 5.2.2. * * Notice that current implementation does not handle properly * 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; /** 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 */ } cwa_tlv_t; /*********************** utility functions ************************/ /** * Dump an APDU before SM translation. * * This is mainly for debugging purposes. programmer should disable * this function in a production environment, as APDU will be shown * in text-plain on debug traces * * @param card Pointer to card driver data structure * @param apdu APDU to be encoded, or APDU response after decoded * @param flag 0: APDU is to be encoded: 1; APDU decoded response */ static void cwa_trace_apdu(sc_card_t * card, sc_apdu_t * apdu, int flag) { char buf[2048]; if (!card || !card->ctx || !apdu || card->ctx->debug < SC_LOG_DEBUG_NORMAL) return; if (flag == 0) { /* apdu command */ if (apdu->datalen > 0) { /* apdu data to show */ 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, apdu->lc, apdu->le, apdu->datalen, buf); } else { /* apdu data field is empty */ 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 (NO DATA)\n======================================================================\n", apdu->cla, apdu->ins, apdu->p1, apdu->p2, apdu->lc, apdu->le); } } else { /* apdu response */ 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); } } /** * Increase send sequence counter SSC. * * @param card smart card info structure * @return SC_SUCCESS if ok; else error code * * TODO: to further study: what about using bignum arithmetic? */ static int cwa_increase_ssc(sc_card_t * card) { int n; struct sm_cwa_session * sm = &card->sm_ctx.info.session.cwa; /* preliminary checks */ if (!card || !card->ctx ) return SC_ERROR_INVALID_ARGUMENTS; LOG_FUNC_CALLED(card->ctx); /* u8 arithmetic; exit loop if no carry */ sc_log(card->ctx, "Curr SSC: '%s'", sc_dump_hex(sm->ssc, 8)); for (n = 7; n >= 0; n--) { sm->ssc[n]++; if ((sm->ssc[n]) != 0x00) break; } sc_log(card->ctx, "Next SSC: '%s'", sc_dump_hex(sm->ssc, 8)); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } /** * ISO 7816 padding. * * Adds an 0x80 at the end of buffer and as many zeroes to get len * multiple of 8 * Buffer must be long enough to store additional bytes * * @param buffer where to compose data * @param len pointer to buffer length */ static void cwa_iso7816_padding(u8 * buf, size_t * buflen) { buf[*buflen] = 0x80; (*buflen)++; for (; *buflen & 0x07; (*buflen)++) buf[*buflen] = 0x00; } /** * compose a BER-TLV data in provided buffer. * * Multibyte tag id are not supported * Also multibyte id 0x84 is unhandled * * 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 * * @param card card info structure * @param tag tag id * @param len data length * @param value data buffer * @param out pointer to dest data * @param outlen length of composed tlv data * @return SC_SUCCESS if ok; else error */ static int cwa_compose_tlv(sc_card_t * card, u8 tag, size_t len, u8 * data, u8 ** out, size_t * outlen) { u8 *pt; size_t size; sc_context_t *ctx; /* preliminary checks */ if (!card || !card->ctx || !out || !outlen) return SC_ERROR_INVALID_ARGUMENTS; /* commodity vars */ ctx = card->ctx; LOG_FUNC_CALLED(ctx); pt = *out; size = *outlen; /* assume tag id is not multibyte */ *(pt + size++) = tag; /* evaluate tag length value according iso7816-4 sect 5.2.2 */ if (len < 0x80) { *(pt + size++) = len; } else if (len < 0x00000100) { *(pt + size++) = 0x81; *(pt + size++) = 0xff & len; } else if (len < 0x00010000) { *(pt + size++) = 0x82; *(pt + size++) = 0xff & (len >> 8); *(pt + size++) = 0xff & len; } else if (len < 0x01000000) { *(pt + size++) = 0x83; *(pt + size++) = 0xff & (len >> 16); *(pt + size++) = 0xff & (len >> 8); *(pt + size++) = 0xff & len; } else { /* do not handle tag length 0x84 */ LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); } /* copy remaining data to buffer */ if (len != 0) memcpy(pt + size, data, len); size += len; *outlen = size; LOG_FUNC_RETURN(ctx, SC_SUCCESS); } /** * 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 always tag length = 1 byte * * FIXME use `sc_asn1_read_tag` or similar instead * * @param card card info structure * @param data Buffer to look for tlv into * @param datalen Buffer len * @param tlv array of TLV structure to store results into * @return SC_SUCCESS if OK; else error code */ static int cwa_parse_tlv(sc_card_t * card, u8 * buffer, size_t datalen, cwa_tlv_t tlv_array[] ) { size_t n = 0; size_t next = 0; sc_context_t *ctx = NULL; /* preliminary checks */ if (!card || !card->ctx) return SC_ERROR_INVALID_ARGUMENTS; /* 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)) { case CWA_SM_PLAIN_TAG: tlv = &tlv_array[0]; break; /* 0x81 Plain */ case CWA_SM_CRYPTO_TAG: tlv = &tlv_array[1]; break; /* 0x87 Crypto */ case CWA_SM_MAC_TAG: tlv = &tlv_array[2]; break; /* 0x8E MAC CC */ case CWA_SM_STATUS_TAG: 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)); 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;; 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; } LOG_FUNC_RETURN(ctx, SC_SUCCESS); /* mark no error */ } /*********************** authentication routines *******************/ /** * Verify certificates provided by 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 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 read from card * @param icc_ca icc certificate from card * @return SC_SUCCESS if verification is ok; else error code */ static int cwa_verify_icc_certificates(sc_card_t * card, cwa_provider_t * provider, X509 * sub_ca_cert, X509 * icc_cert) { char *msg = NULL; int res = SC_SUCCESS; EVP_PKEY *root_ca_key = NULL; EVP_PKEY *sub_ca_key = NULL; sc_context_t *ctx = NULL; /* safety check */ if (!card || !card->ctx || !provider) return SC_ERROR_INVALID_ARGUMENTS; ctx = card->ctx; LOG_FUNC_CALLED(ctx); if (!sub_ca_cert || !icc_cert) /* check received arguments */ LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); /* retrieve root ca pkey from provider */ res = provider->cwa_get_root_ca_pubkey(card, &root_ca_key); if (res != SC_SUCCESS) { msg = "Cannot get root CA public key"; res = SC_ERROR_INTERNAL; goto verify_icc_certificates_end; } /* 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 */ 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; } /* arriving here means certificate verification success */ res = SC_SUCCESS; verify_icc_certificates_end: if (root_ca_key) EVP_PKEY_free(root_ca_key); if (sub_ca_key) EVP_PKEY_free(sub_ca_key); 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 * (Perform Security Operation: Verify Certificate ) * * @param card pointer to card data * @param cert Certificate in CVC format * @param len length of CVC certificate * @return SC_SUCCESS if ok; else error code */ static int cwa_verify_cvc_certificate(sc_card_t * card, const u8 * cert, size_t len) { sc_apdu_t apdu; int result = SC_SUCCESS; sc_context_t *ctx = NULL; /* safety check */ if (!card || !card->ctx) return SC_ERROR_INVALID_ARGUMENTS; ctx = card->ctx; LOG_FUNC_CALLED(ctx); if (!cert || (len <= 0)) /* check received arguments */ LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); /* compose apdu for Perform Security Operation (Verify cert) cmd */ dnie_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x2A, 0x00, 0xAE, 0, len, NULL, 0, cert, len); /* send composed apdu and parse result */ result = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(ctx, result, "Verify CVC certificate failed"); result = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_FUNC_RETURN(ctx, result); } /** * Alternate implementation for set_security environment. * * 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 p1 apdu P1 parameter * @param p2 apdu P2 parameter * @param buffer raw data to be inserted in apdu * @param length size of buffer * @return SC_SUCCESS if ok; else error code */ static int cwa_set_security_env(sc_card_t * card, u8 p1, u8 p2, u8 * buffer, size_t length) { sc_apdu_t apdu; int result = SC_SUCCESS; sc_context_t *ctx = NULL; /* safety check */ if (!card || !card->ctx) return SC_ERROR_INVALID_ARGUMENTS; ctx = card->ctx; LOG_FUNC_CALLED(ctx); if (!buffer || (length <= 0)) /* check received arguments */ LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); /* compose apdu for Manage Security Environment cmd */ dnie_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, p1, p2, 0, length, NULL, 0, buffer, length); /* send composed apdu and parse result */ result = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(ctx, result, "SM Set Security Environment failed"); result = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_FUNC_RETURN(ctx, result); } /** * SM internal authenticate. * * Internal (Card) authentication (let the card verify sent ifd certs) * * @param card pointer to card data * @param sig signature buffer * @param dig_len signature buffer length * @param data data to be sent in apdu * @param datalen length of data to send * @return SC_SUCCESS if OK: else error code */ static int cwa_internal_auth(sc_card_t * card, u8 * sig, size_t sig_len, u8 * data, size_t datalen) { sc_apdu_t apdu; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; int result = SC_SUCCESS; sc_context_t *ctx = NULL; /* safety check */ if (!card || !card->ctx) return SC_ERROR_INVALID_ARGUMENTS; ctx = card->ctx; LOG_FUNC_CALLED(ctx); if (!data || (datalen <= 0)) /* check received arguments */ LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); /* compose apdu for Internal Authenticate cmd */ dnie_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x88, 0x00, 0x00, 0x80, datalen, rbuf, sizeof(rbuf), data, datalen); /* send composed apdu and parse result */ result = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(ctx, result, "SM internal auth failed"); result = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(ctx, result, "SM internal auth invalid response"); if (apdu.resplen != sig_len) /* invalid number of bytes received */ LOG_FUNC_RETURN(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); memcpy(sig, apdu.resp, apdu.resplen); /* copy result to buffer */ LOG_FUNC_RETURN(ctx, SC_SUCCESS); } /** * Compose signature data for external auth according CWA-14890. * * This code prepares data to be sent to ICC for external * authentication procedure * * Store resulting data into sm->sig * * @param card pointer to st_card_t card data information * @param icc_pubkey public key of card * @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, EVP_PKEY *icc_pubkey, EVP_PKEY *ifd_privkey, u8 * sig, size_t sig_len) { /* we have to compose following message: data = E[PK.ICC.AUT](SIGMIN) SIGMIN = min ( SIG, N.IFD-SIG ) SIG= DS[SK.IFD.AUT] ( 0x6A || - padding according iso 9796-2 PRND2 || - (74 bytes) random data to make buffer 128 bytes length Kifd || - (32 bytes)- ifd random generated key sha1_hash( 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 = NULL; /* to store error messages */ int res = SC_SUCCESS; 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; 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) return SC_ERROR_INVALID_ARGUMENTS; ctx = card->ctx; LOG_FUNC_CALLED(ctx); /* check received arguments */ if (!icc_pubkey || !ifd_privkey || !sm) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); buf1 = calloc(128, sizeof(u8)); buf2 = calloc(128, sizeof(u8)); buf3 = calloc(128, sizeof(u8)); sha_buf = calloc(74 + 32 + 8 + 8, sizeof(u8)); sha_data = calloc(SHA_DIGEST_LENGTH, sizeof(u8)); /* alloc() resources */ if (!buf1 || !buf2 || !buf3 || !sha_buf || !sha_data) { msg = "prepare external auth: calloc error"; res = SC_ERROR_OUT_OF_MEMORY; goto prepare_external_auth_end; } /* compose buffer data */ buf3[0] = 0x6A; /* iso padding */ 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 */ memcpy(sha_buf + 74, buf3 + 1 + 74, 32); /* copy kifd into sha_buf */ memcpy(sha_buf + 74 + 32, sm->icc.rnd, 8); /* copy 8 byte icc challenge */ memcpy(sha_buf + 74 + 32 + 8, sm->icc.sn, 8); /* copy serialnr, 8 bytes */ SHA1(sha_buf, 74 + 32 + 8 + 8, sha_data); /* copy hashed data into buffer */ memcpy(buf3 + 1 + 74 + 32, sha_data, SHA_DIGEST_LENGTH); buf3[127] = 0xBC; /* iso padding */ /* 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, (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; } #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 */ 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: BN_free(bn); BN_free(bnsub); if (buf1) { sc_mem_clear(buf1, 128); free(buf1); } if (buf2) { sc_mem_clear(buf2, 128); free(buf2); } if (buf3) { sc_mem_clear(buf3, 128); free(buf3); } if (sha_buf) { sc_mem_clear(sha_buf, 74 + 32 + 8 + 1 + 7); free(sha_buf); } free(sha_data); #if OPENSSL_VERSION_NUMBER >= 0x30000000L BN_clear_free(ifd_privkey_n); #endif if (res != SC_SUCCESS) { sc_log(ctx, "%s", msg); } LOG_FUNC_RETURN(ctx, res); } /** * SM external authenticate. * * Perform external (IFD) authenticate procedure (8.4.1.2) * * @param card pointer to card data * @param sig signature buffer * @param sig signature buffer length * @return SC_SUCCESS if OK: else error code */ static int cwa_external_auth(sc_card_t * card, u8 * sig, size_t sig_len) { sc_apdu_t apdu; int result = SC_SUCCESS; sc_context_t *ctx = NULL; /* safety check */ if (!card || !card->ctx) return SC_ERROR_INVALID_ARGUMENTS; ctx = card->ctx; LOG_FUNC_CALLED(ctx); /* compose apdu for External Authenticate cmd */ dnie_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x82, 0x00, 0x00, 0, sig_len, NULL, 0, sig, sig_len); /* send composed apdu and parse result */ result = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(ctx, result, "SM external auth failed"); result = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(ctx, result, "SM external auth invalid response"); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } /** * SM creation of session keys. * * Compute Kenc,Kmac, and SSC and store it into sm data * * @param card pointer to sc_card_t data * @return SC_SUCCESS if ok; else error code */ static int cwa_compute_session_keys(sc_card_t * card) { char *msg = NULL; int n = 0; int res = SC_SUCCESS; u8 *kseed; /* to compose kifd ^ kicc */ u8 *data; /* to compose kenc and kmac to be hashed */ u8 *sha_data; /* to store hash result */ u8 kenc[4] = { 0x00, 0x00, 0x00, 0x01 }; u8 kmac[4] = { 0x00, 0x00, 0x00, 0x02 }; sc_context_t *ctx = NULL; struct sm_cwa_session * sm = &card->sm_ctx.info.session.cwa; /* safety check */ if (!card || !card->ctx) return SC_ERROR_INVALID_ARGUMENTS; ctx = card->ctx; LOG_FUNC_CALLED(ctx); /* Just a literal transcription of cwa14890-1 sections 8.7.2 to 8.9 */ kseed = calloc(32, sizeof(u8)); data = calloc(32 + 4, sizeof(u8)); sha_data = calloc(SHA_DIGEST_LENGTH, sizeof(u8)); if (!kseed || !data || !sha_data) { msg = "Compute Session Keys: calloc() failed"; res = SC_ERROR_OUT_OF_MEMORY; goto compute_session_keys_end; } /* compose kseed (cwa-14890-1 sect 8.7.2) */ for (n = 0; n < 32; n++) *(kseed + n) = sm->icc.k[n] ^ sm->ifd.k[n]; /* evaluate kenc (cwa-14890-1 sect 8.8) */ memcpy(data, kseed, 32); memcpy(data + 32, kenc, 4); SHA1(data, 32 + 4, sha_data); memcpy(sm->session_enc, sha_data, 16); /* kenc=16 fsb sha((kifd^kicc)||00000001) */ /* evaluate kmac */ memset(data, 0, 32 + 4); memset(sha_data, 0, SHA_DIGEST_LENGTH); /* clear buffers */ memcpy(data, kseed, 32); memcpy(data + 32, kmac, 4); SHA1(data, 32 + 4, sha_data); memcpy(sm->session_mac, sha_data, 16); /* kmac=16 fsb sha((kifd^kicc)||00000002) */ /* evaluate send sequence counter (cwa-14890-1 sect 8.9 & 9.6 */ memcpy(sm->ssc, sm->icc.rnd + 4, 4); /* 4 least significant bytes of rndicc */ memcpy(sm->ssc + 4, sm->ifd.rnd + 4, 4); /* 4 least significant bytes of rndifd */ /* arriving here means process ok */ res = SC_SUCCESS; compute_session_keys_end: if (kseed) { sc_mem_clear(kseed, 32); free(kseed); } if (data) { sc_mem_clear(data, 32 + 4); free(data); } free(sha_data); if (res != SC_SUCCESS) sc_log(ctx, "%s", msg); else { sc_log(ctx, "Kenc: %s", sc_dump_hex(sm->session_enc, 16)); sc_log(ctx, "Kmac: %s", sc_dump_hex(sm->session_mac, 16)); sc_log(ctx, "SSC: %s", sc_dump_hex(sm->ssc, 8)); } LOG_FUNC_RETURN(ctx, res); } /* * Compare signature for internal auth procedure. * * @param data Received data to be checked * @param dlen data length * @param expected results * @return SC_SUCCESS or error code */ static int cwa_compare_signature(u8 * data, size_t dlen, u8 * ifd_data) { u8 *buf = calloc(74 + 32 + 32, sizeof(u8)); u8 *sha = calloc(SHA_DIGEST_LENGTH, sizeof(u8)); int res = SC_SUCCESS; if (!buf || !sha) { res = SC_ERROR_OUT_OF_MEMORY; goto compare_signature_end; } res = SC_ERROR_INVALID_DATA; if (dlen != 128) goto compare_signature_end; /* check length */ if (data[0] != 0x6a) goto compare_signature_end; /* iso 9796-2 padding */ if (data[127] != 0xBC) goto compare_signature_end; /* iso 9796-2 padding */ memcpy(buf, data + 1, 74 + 32); memcpy(buf + 74 + 32, ifd_data, 16); SHA1(buf, 74 + 32 + 16, sha); if (memcmp(data + 127 - SHA_DIGEST_LENGTH, sha, SHA_DIGEST_LENGTH) == 0) res = SC_SUCCESS; compare_signature_end: free(buf); free(sha); return res; } /** * check the result of internal_authenticate operation. * * Checks icc received data from internal auth procedure against * expected results * * @param card Pointer to sc_card_t data * @param icc_pubkey icc public key * @param ifd_privkey ifd private key * @param ifdbuf buffer containing ( RND.IFD || SN.IFD ) * @param ifdlen buffer length; should be 16 * @param sig signature buffer * @param sig_len signature buffer length * @return SC_SUCCESS if ok; else error code */ static int cwa_verify_internal_auth(sc_card_t * card, EVP_PKEY *icc_pubkey, EVP_PKEY *ifd_privkey, u8 * ifdbuf, size_t ifdlen, u8 * sig, size_t sig_len) { int res = SC_SUCCESS; char *msg = NULL; 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 */ size_t len1 = 128, len2 = 128, len3 = 128; BIGNUM *bn = NULL; BIGNUM *sigbn = NULL; sc_context_t *ctx = NULL; 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; ctx = card->ctx; LOG_FUNC_CALLED(ctx); if (!ifdbuf || (ifdlen != 16)) { res = SC_ERROR_INVALID_ARGUMENTS; msg = "Null buffers received as parameters"; goto verify_internal_done; } if (!icc_pubkey || !ifd_privkey) { res = SC_ERROR_SM_NO_SESSION_KEYS; msg = "Either provided icc_pubk or ifd_privk are null"; goto verify_internal_done; } buf1 = (u8 *) calloc(128, sizeof(u8)); /* 128: RSA key len in bytes */ buf2 = (u8 *) calloc(128, sizeof(u8)); buf3 = (u8 *) calloc(128, sizeof(u8)); if (!buf1 || !buf2 || !buf3) { msg = "Verify Signature: calloc() error"; res = SC_ERROR_OUT_OF_MEMORY; goto verify_internal_done; } /* We have received data with this format: sigbuf = E[PK.IFD.AUT](SIGMIN) SIGMIN = min ( SIG, N.ICC-SIG ) SIG= DS[SK.ICC.AUT] ( 0x6A || PRND1 || Kicc || sha1_hash(PRND1 || Kicc || RND.IFD || SN.IFD) || 0xBC ) So we should reverse the process and try to get valid results */ /* decrypt data with our ifd priv 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, 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 */ 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 * So convert buffers to bignums to operate */ 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; } #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) { 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 */ 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"; res = SC_ERROR_INVALID_DATA; goto verify_internal_done; } /* arriving here means OK: complete data structures */ verify_internal_ok: memcpy(sm->icc.k, buf3 + 1 + 74, 32); /* extract Kicc from buf3 */ res = SC_SUCCESS; verify_internal_done: 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 channel creation. * It closely follows cwa standard, with a minor modification: * - 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 process (this behavior * is also defined in standard) * * Based on Several documents: * - "Understanding the DNIe" * - "Manual de comandos del DNIe" * - ISO7816-4 and CWA14890-{1,2} * * @param card card info structure * @param provider cwa14890 info provider * @param flag requested init method ( OFF, COLD, WARM ) * @return SC_SUCCESS if OK; else error code */ int cwa_create_secure_channel(sc_card_t * card, cwa_provider_t * provider, int flag) { u8 *cert = NULL; size_t certlen; int res = SC_SUCCESS; char *msg = "Success"; /* data to get and parse certificates */ X509 *icc_cert = NULL; X509 *ca_cert = NULL; EVP_PKEY *icc_pubkey = NULL; EVP_PKEY *ifd_privkey = NULL; sc_context_t *ctx = NULL; struct sm_cwa_session * sm = &card->sm_ctx.info.session.cwa; u8 sig[128]; /* several buffer and buffer pointers */ u8 *buffer = NULL; size_t bufferlen; u8 *tlv = NULL; /* buffer to compose TLV messages */ size_t tlvlen = 0; u8 rndbuf[16]; /* 8 RND.IFD + 8 SN.IFD */ /* preliminary checks */ if (!card || !card->ctx ) return SC_ERROR_INVALID_ARGUMENTS; if (!provider) return SC_ERROR_SM_NOT_INITIALIZED; /* commodity vars */ ctx = card->ctx; LOG_FUNC_CALLED(ctx); /* check requested initialization method */ switch (flag) { 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); case CWA_SM_ON: /* force sm initialization process */ sc_log(ctx, "CWA SM initialization requested"); break; default: sc_log(ctx, "Invalid provided SM initialization flag"); LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); } /* OK: lets start process */ /* call provider pre-operation method */ sc_log(ctx, "CreateSecureChannel pre-operations"); if (provider->cwa_create_pre_ops) { res = provider->cwa_create_pre_ops(card, provider); if (res != SC_SUCCESS) { msg = "Create SM: provider pre_ops() failed"; sc_log(ctx, "%s", msg); goto csc_end; } } /* retrieve icc serial number */ sc_log(ctx, "Retrieve ICC serial number"); if (provider->cwa_get_sn_icc) { res = provider->cwa_get_sn_icc(card); if (res != SC_SUCCESS) { msg = "Retrieve ICC failed"; sc_log(ctx, "%s", msg); goto csc_end; } } else { msg = "Don't know how to obtain ICC serial number"; sc_log(ctx, "%s", msg); res = SC_ERROR_INTERNAL; goto csc_end; } /* * Notice that this code inverts ICC and IFD certificate standard * checking sequence. */ /* Read Intermediate CA from card */ if (!provider->cwa_get_icc_intermediate_ca_cert) { sc_log(ctx, "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"); res = provider->cwa_get_icc_intermediate_ca_cert(card, &ca_cert); if (res != SC_SUCCESS) { msg = "Cannot get ICC intermediate CA certificate from provider"; goto csc_end; } } /* Read ICC certificate from card */ sc_log(ctx, "Step 8.4.1.8: Retrieve ICC certificate"); res = provider->cwa_get_icc_cert(card, &icc_cert); if (res != SC_SUCCESS) { msg = "Cannot get ICC certificate from provider"; goto csc_end; } /* Verify icc Card certificate chain */ /* Notice that Some implementations doesn't verify cert chain * but simply verifies that icc_cert is a valid certificate */ if (ca_cert) { sc_log(ctx, "Verifying ICC certificate chain"); res = cwa_verify_icc_certificates(card, provider, ca_cert, icc_cert); if (res != SC_SUCCESS) { res = SC_ERROR_SM_AUTHENTICATION_FAILED; msg = "Icc Certificates verification failed"; goto csc_end; } } else { sc_log(ctx, "Cannot verify Certificate chain. skip step"); } /* Extract public key from ICC certificate */ 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, "Step 8.4.1.2: Select Root CA in card for IFD cert verification"); res = provider->cwa_get_root_ca_pubkey_ref(card, &buffer, &bufferlen); if (res != SC_SUCCESS) { msg = "Cannot get Root CA key reference from provider"; goto csc_end; } tlvlen = 0; tlv = calloc(10 + bufferlen, sizeof(u8)); if (!tlv) { msg = "calloc error"; res = SC_ERROR_OUT_OF_MEMORY; goto csc_end; } res = cwa_compose_tlv(card, 0x83, bufferlen, buffer, &tlv, &tlvlen); if (res != SC_SUCCESS) { msg = "Cannot compose tlv for setting Root CA key reference"; goto csc_end; } res = cwa_set_security_env(card, 0x81, 0xB6, tlv, tlvlen); if (res != SC_SUCCESS) { msg = "Select Root CA key ref failed"; goto csc_end; } /* Send IFD intermediate CA in CVC format C_CV_CA */ sc_log(ctx, "Step 8.4.1.3: Send CVC IFD intermediate CA Cert for ICC verification"); res = provider->cwa_get_cvc_ca_cert(card, &cert, &certlen); if (res != SC_SUCCESS) { msg = "Get CVC CA cert from provider failed"; goto csc_end; } res = cwa_verify_cvc_certificate(card, cert, certlen); if (res != SC_SUCCESS) { msg = "Verify CVC CA failed"; goto csc_end; } /* select public key reference for sent IFD intermediate CA certificate */ sc_log(ctx, "Step 8.4.1.4: Select Intermediate CA pubkey ref for ICC verification"); res = provider->cwa_get_intermediate_ca_pubkey_ref(card, &buffer, &bufferlen); if (res != SC_SUCCESS) { msg = "Cannot get intermediate CA key reference from provider"; goto csc_end; } tlvlen = 0; free(tlv); tlv = calloc(10 + bufferlen, sizeof(u8)); if (!tlv) { msg = "calloc error"; res = SC_ERROR_OUT_OF_MEMORY; goto csc_end; } res = cwa_compose_tlv(card, 0x83, bufferlen, buffer, &tlv, &tlvlen); if (res != SC_SUCCESS) { msg = "Cannot compose tlv for setting intermediate CA key reference"; goto csc_end; } res = cwa_set_security_env(card, 0x81, 0xB6, tlv, tlvlen); if (res != SC_SUCCESS) { msg = "Select CVC CA pubk failed"; goto csc_end; } /* 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); if (res != SC_SUCCESS) { msg = "Get CVC IFD cert from provider failed"; goto csc_end; } res = cwa_verify_cvc_certificate(card, cert, certlen); if (res != SC_SUCCESS) { msg = "Verify CVC IFD failed"; goto csc_end; } /* remember that this code changes IFD and ICC Cert verification steps */ /* select public key of ifd certificate and icc private key */ sc_log(ctx, "Step 8.4.1.9: Send IFD pubk and ICC privk key references for Internal Auth"); res = provider->cwa_get_ifd_pubkey_ref(card, &buffer, &bufferlen); if (res != SC_SUCCESS) { msg = "Cannot get ifd public key reference from provider"; goto csc_end; } tlvlen = 0; free(tlv); tlv = calloc(10 + bufferlen, sizeof(u8)); if (!tlv) { msg = "calloc error"; res = SC_ERROR_OUT_OF_MEMORY; goto csc_end; } res = cwa_compose_tlv(card, 0x83, bufferlen, buffer, &tlv, &tlvlen); if (res != SC_SUCCESS) { msg = "Cannot compose tlv for setting ifd pubkey reference"; goto csc_end; } res = provider->cwa_get_icc_privkey_ref(card, &buffer, &bufferlen); if (res != SC_SUCCESS) { msg = "Cannot get icc private key reference from provider"; goto csc_end; } /* add this tlv to old one; do not call calloc */ res = cwa_compose_tlv(card, 0x84, bufferlen, buffer, &tlv, &tlvlen); if (res != SC_SUCCESS) { msg = "Cannot compose tlv for setting ifd pubkey reference"; goto csc_end; } res = cwa_set_security_env(card, 0xC1, 0xA4, tlv, tlvlen); if (res != SC_SUCCESS) { msg = "Select CVC IFD pubk failed"; goto csc_end; } /* 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); if (res != SC_SUCCESS) { msg = "Cannot get ifd serial number from provider"; goto csc_end; } /* 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); if (res != SC_SUCCESS) { msg = "Internal auth cmd failed"; goto csc_end; } /* retrieve ifd private key from provider */ res = provider->cwa_get_ifd_privkey(card, &ifd_privkey); if (res != SC_SUCCESS) { msg = "Cannot retrieve IFD private key from provider"; res = SC_ERROR_SM_NO_SESSION_KEYS; goto csc_end; } /* verify received signature */ sc_log(ctx, "Verify Internal Auth command response"); 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 ); if (res != SC_SUCCESS) { msg = "Internal Auth Verify failed"; goto csc_end; } /* get challenge: retrieve 8 random bytes from card */ sc_log(ctx, "Step 8.4.1.11: Prepare External Auth: Get Challenge"); 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, icc_pubkey, ifd_privkey, sig, 128); if (res != SC_SUCCESS) { msg = "Prepare external auth failed"; goto csc_end; } /* External (IFD) authentication */ sc_log(ctx, "Step 8.4.1.12: Perform External (IFD) Authentication"); res = cwa_external_auth(card, sig, 128); if (res != SC_SUCCESS) { msg = "External auth cmd failed"; goto csc_end; } /* Session key generation */ sc_log(ctx, "Step 8.4.2: Compute Session Keys"); res = cwa_compute_session_keys(card); if (res != SC_SUCCESS) { msg = "Session Key generation failed"; goto csc_end; } /* call provider post-operation method */ sc_log(ctx, "CreateSecureChannel post-operations"); if (provider->cwa_create_post_ops) { res = provider->cwa_create_post_ops(card, provider); if (res != SC_SUCCESS) { sc_log(ctx, "Create SM: provider post_ops() failed"); goto csc_end; } } /* arriving here means ok: cleanup */ res = SC_SUCCESS; csc_end: free(tlv); 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); card->sm_ctx.sm_mode = SM_MODE_NONE; } else { card->sm_ctx.sm_mode = SM_MODE_TRANSMIT; } LOG_FUNC_RETURN(ctx, res); } /******************* SM internal APDU encoding / decoding functions ******/ /** * Encode an APDU. * * Calling this functions means that It's has been verified * That source apdu needs encoding * Based on section 9 of CWA-14890 and Sect 6 of iso7816-4 standards * And DNIe's manual * * @param card card info structure * @param sm Secure Messaging state information * @param from APDU to be encoded * @param to where to store encoded apdu * @return SC_SUCCESS if ok; else error code */ int cwa_encode_apdu(sc_card_t * card, cwa_provider_t * provider, sc_apdu_t * from, sc_apdu_t * to) { u8 *apdubuf = NULL; /* to store resulting apdu */ size_t apdulen, tlv_len; u8 *ccbuf = NULL; /* where to store data to eval cryptographic checksum CC */ size_t cclen = 0; u8 macbuf[8]; /* to store and compute CC */ char *msg = NULL; size_t i, j; /* for xor loops */ int res = SC_SUCCESS; sc_context_t *ctx = NULL; struct sm_cwa_session * sm_session = &card->sm_ctx.info.session.cwa; 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; ctx = card->ctx; LOG_FUNC_CALLED(ctx); /* check remaining arguments */ if (!from || !to || !sm_session) LOG_FUNC_RETURN(ctx, SC_ERROR_SM_NOT_INITIALIZED); if (card->sm_ctx.sm_mode != SM_MODE_TRANSMIT) LOG_FUNC_RETURN(ctx, SC_ERROR_SM_INVALID_LEVEL); /* reserve extra bytes for padding and tlv header */ msgbuf = calloc(12 + from->lc, sizeof(u8)); /* to encrypt apdu data */ cryptbuf = calloc(12 + from->lc, sizeof(u8)); if (!msgbuf || !cryptbuf) { res = SC_ERROR_OUT_OF_MEMORY; goto err; } /* check if APDU is already encoded */ if ((from->cla & 0x0C) != 0) { memcpy(to, from, sizeof(sc_apdu_t)); res = SC_SUCCESS; /* already encoded */ goto encode_end; } if (from->ins == 0xC0) { memcpy(to, from, sizeof(sc_apdu_t)); res = SC_SUCCESS; /* dont encode GET Response cmd */ goto encode_end; } /* trace APDU before encoding process */ cwa_trace_apdu(card, from, 0); /* reserve enough space for apdulen+tlv bytes * to-be-crypted buffer and result apdu buffer */ /* TODO DEE add 4 more bytes for testing.... */ apdubuf = calloc(MAX(SC_MAX_APDU_BUFFER_SIZE, 20 + from->datalen), sizeof(u8)); ccbuf = calloc(MAX(SC_MAX_APDU_BUFFER_SIZE, 20 + from->datalen), sizeof(u8)); /* 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; } /* set up data on destination apdu */ to->cse = SC_APDU_CASE_4_SHORT; to->cla = from->cla | 0x0C; /* mark apdu as encoded */ to->ins = from->ins; to->p1 = from->p1; to->p2 = from->p2; to->le = from->le; if (!to->le) to->le = 255; to->lc = 0; /* to be evaluated */ /* fill buffer with header info */ *(ccbuf + cclen++) = to->cla; *(ccbuf + cclen++) = to->ins; *(ccbuf + cclen++) = to->p1; *(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) { 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, &len); dlen = (int)len; /* start kriptbuff with iso padding indicator */ *cryptbuf = 0x01; 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); 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; } } /* 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 */ cwa_iso7816_padding(ccbuf, &cclen); /* sc_log(ctx,"data to compose mac: %s",sc_dump_hex(ccbuf,cclen)); */ /* compute MAC Cryptographic Checksum using kmac and increased SSC */ res = cwa_increase_ssc(card); /* increase send sequence counter */ if (res != SC_SUCCESS) { msg = "Error in computing SSC"; goto encode_end; } 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 */ 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 */ 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 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 */ to->lc = apdulen; to->data = apdubuf; to->datalen = apdulen; /* that's all folks */ res = SC_SUCCESS; goto encode_end_apdu_valid; err: encode_end: free(apdubuf); if (from->resp != to->resp) free(to->resp); encode_end_apdu_valid: 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); LOG_FUNC_RETURN(ctx, res); } /** * Decode an APDU response. * * Calling this functions means that It's has been verified * That apdu response comes in TLV encoded format and needs decoding * Based on section 9 of CWA-14890 and Sect 6 of iso7816-4 standards * And DNIe's manual * * @param card card info structure * @param sm Secure Messaging state information * @param from APDU with response to be decoded * @param to where to store decoded apdu * @return SC_SUCCESS if ok; else error code */ int cwa_decode_response(sc_card_t * card, cwa_provider_t * provider, sc_apdu_t * apdu) { size_t i, j, tlv_len; cwa_tlv_t tlv_array[4]; cwa_tlv_t *p_tlv = &tlv_array[0]; /* to store plain data (Tag 0x81) */ cwa_tlv_t *e_tlv = &tlv_array[1]; /* to store pad encoded data (Tag 0x87) */ cwa_tlv_t *m_tlv = &tlv_array[2]; /* to store mac CC (Tag 0x8E) */ cwa_tlv_t *s_tlv = &tlv_array[3]; /* to store sw1-sw2 status (Tag 0x99) */ u8 *buffer = NULL; /* buffer for data. pointers to this buffer are in tlv_array */ u8 *ccbuf = NULL; /* buffer for mac CC calculation */ size_t cclen = 0; /* ccbuf len */ u8 macbuf[8]; /* where to calculate mac */ size_t resplen = 0; /* respbuf length */ 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; ctx = card->ctx; LOG_FUNC_CALLED(ctx); /* check remaining arguments */ if ((apdu == NULL) || (sm_session == NULL)) LOG_FUNC_RETURN(ctx, SC_ERROR_SM_NOT_INITIALIZED); if (card->sm_ctx.sm_mode != SM_MODE_TRANSMIT) LOG_FUNC_RETURN(ctx, SC_ERROR_SM_INVALID_LEVEL); /* cwa14890 sect 9.3: check SW1 or SW2 for SM related errors */ if (apdu->sw1 == 0x69) { if ((apdu->sw2 == 0x88) || (apdu->sw2 == 0x87)) { /* configure the driver to re-establish the SM */ msg = "SM related errors in APDU response"; cwa_create_secure_channel(card, provider, CWA_SM_OFF); res = SC_ERROR_SECURITY_STATUS_NOT_SATISFIED; goto response_decode_end; } } /* if response is null/empty assume unencoded apdu */ if (!apdu->resp || (apdu->resplen == 0)) { sc_log(ctx, "Empty APDU response: assume not cwa encoded"); return SC_SUCCESS; } /* checks if apdu response needs decoding by checking tags in response */ switch (*apdu->resp) { case CWA_SM_PLAIN_TAG: case CWA_SM_CRYPTO_TAG: case CWA_SM_MAC_TAG: case CWA_SM_LE_TAG: case CWA_SM_STATUS_TAG: break; /* cwa tags found: continue decoding */ default: /* else apdu response seems not to be cwa encoded */ sc_log(card->ctx, "APDU Response seems not to be cwa encoded"); return SC_SUCCESS; /* let process continue */ } /* parse response to find TLV's data and check results */ memset(tlv_array, 0, 4 * sizeof(cwa_tlv_t)); /* create buffer and copy data into */ buffer = calloc(apdu->resplen, sizeof(u8)); if (!buffer) { msg = "Cannot allocate space for response buffer"; res = SC_ERROR_OUT_OF_MEMORY; goto response_decode_end; } memcpy(buffer, apdu->resp, apdu->resplen); res = cwa_parse_tlv(card, buffer, apdu->resplen, tlv_array); if (res != SC_SUCCESS) { msg = "Error in TLV parsing"; goto response_decode_end; } /* check consistency of received TLV's */ if (p_tlv->buf && e_tlv->buf) { msg = "Plain and Encoded data are mutually exclusive in apdu response"; res = SC_ERROR_INVALID_DATA; goto response_decode_end; } if (!m_tlv->buf) { msg = "No MAC TAG found in apdu response"; res = SC_ERROR_INVALID_DATA; goto response_decode_end; } tlv_len = (card->atr.value[15] >= DNIE_30_VERSION)? 8 : 4; if (m_tlv->len != tlv_len) { msg = "Invalid MAC TAG Length"; res = SC_ERROR_INVALID_DATA; goto response_decode_end; } /* compose buffer to evaluate mac */ /* reserve enough space for data+status+padding */ ccbuf = calloc(e_tlv->buflen + s_tlv->buflen + p_tlv->buflen + 8, sizeof(u8)); if (!ccbuf) { msg = "Cannot allocate space for mac checking"; res = SC_ERROR_OUT_OF_MEMORY; goto response_decode_end; } /* copy data into buffer */ cclen = 0; if (e_tlv->buf) { /* encoded data */ memcpy(ccbuf, e_tlv->buf, e_tlv->buflen); cclen = e_tlv->buflen; } if (p_tlv->buf) { /* plain data */ memcpy(ccbuf, p_tlv->buf, p_tlv->buflen); cclen += p_tlv->buflen; } if (s_tlv->buf) { /* response status */ if (s_tlv->len != 2) { msg = "Invalid SW TAG length"; res = SC_ERROR_INVALID_DATA; goto response_decode_end; } memcpy(ccbuf + cclen, s_tlv->buf, s_tlv->buflen); cclen += s_tlv->buflen; apdu->sw1 = s_tlv->data[0]; apdu->sw2 = s_tlv->data[1]; } /* if no response status tag, use sw1 and sw2 from apdu */ /* add iso7816 padding */ cwa_iso7816_padding(ccbuf, &cclen); /* evaluate mac by mean of kmac and increased SendSequence Counter SSC */ /* increase SSC */ res = cwa_increase_ssc(card); /* increase send sequence counter */ if (res != SC_SUCCESS) { msg = "Error in computing SSC"; goto response_decode_end; } /* 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 */ 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 */ 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 */ res = memcmp(m_tlv->data, macbuf, 4); /* check first 4 bytes */ if (res != 0) { msg = "Error in MAC CC checking: value doesn't match"; res = SC_ERROR_SM_ENCRYPT_FAILED; goto response_decode_end; } /* allocate response buffer */ resplen = 10 + MAX(p_tlv->len, e_tlv->len); /* estimate response buflen */ if (apdu->resplen < resplen) { msg = "Cannot allocate buffer to store response"; res = SC_ERROR_BUFFER_TOO_SMALL; goto response_decode_end; } apdu->resplen = resplen; /* fill destination response apdu buffer with data */ /* if plain data, just copy TLV data into apdu response */ if (p_tlv->buf) { /* plain data */ memcpy(apdu->resp, p_tlv->data, p_tlv->len); apdu->resplen = p_tlv->len; } /* if encoded data, decode and store into apdu response */ else if (e_tlv->buf) { /* encoded data */ 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"; res = SC_ERROR_INVALID_DATA; goto response_decode_end; } /* first byte is padding info; check value */ if (e_tlv->data[0] != 0x01) { msg = "Encoded TLV: Invalid padding info value"; res = SC_ERROR_INVALID_DATA; goto response_decode_end; } /* prepare keys to decode */ key = sm_session->session_enc; /* decrypt into response buffer * by using 3DES CBC by mean of kenc and iv={0,...0} */ 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!"; res = SC_ERROR_INVALID_DATA; goto response_decode_end; } /* everything ok: remove ending 0x80 from response */ apdu->resplen--; } else apdu->resplen = 0; /* neither plain, nor encoded data */ /* that's all folks */ res = SC_SUCCESS; response_decode_end: sc_evp_cipher_free(alg); EVP_CIPHER_CTX_free(cctx); free(buffer); free(ccbuf); if (msg) { sc_log(ctx, "%s", msg); } else { cwa_trace_apdu(card, apdu, 1); } /* trace apdu response */ LOG_FUNC_RETURN(ctx, res); } /********************* default provider for cwa14890 ****************/ /* pre and post operations */ static int default_create_pre_ops(sc_card_t * card, cwa_provider_t * provider) { return SC_SUCCESS; } static int default_create_post_ops(sc_card_t * card, cwa_provider_t * provider) { return SC_SUCCESS; } static int default_get_root_ca_pubkey(sc_card_t * card, EVP_PKEY ** root_ca_key) { return SC_ERROR_NOT_SUPPORTED; } /* retrieve CVC intermediate CA certificate and length */ static int default_get_cvc_ca_cert(sc_card_t * card, u8 ** cert, size_t * length) { return SC_ERROR_NOT_SUPPORTED; } /* retrieve CVC IFD certificate and length */ static int default_get_cvc_ifd_cert(sc_card_t * card, u8 ** cert, size_t * length) { return SC_ERROR_NOT_SUPPORTED; } static int default_get_ifd_privkey(sc_card_t * card, EVP_PKEY ** ifd_privkey) { return SC_ERROR_NOT_SUPPORTED; } /* get ICC intermediate CA path */ static int default_get_icc_intermediate_ca_cert(sc_card_t * card, X509 ** cert) { return SC_ERROR_NOT_SUPPORTED; } /* get ICC certificate path */ static int default_get_icc_cert(sc_card_t * card, X509 ** cert) { return SC_ERROR_NOT_SUPPORTED; } /* Retrieve key reference for Root CA to validate CVC intermediate CA certs */ static int default_get_root_ca_pubkey_ref(sc_card_t * card, u8 ** buf, size_t * len) { return SC_ERROR_NOT_SUPPORTED; } /* Retrieve key reference for intermediate CA to validate IFD certs */ static int default_get_intermediate_ca_pubkey_ref(sc_card_t * card, u8 ** buf, size_t * len) { return SC_ERROR_NOT_SUPPORTED; } /* Retrieve key reference for IFD certificate */ static int default_get_ifd_pubkey_ref(sc_card_t * card, u8 ** buf, size_t * len) { return SC_ERROR_NOT_SUPPORTED; } /* Retrieve key reference for ICC privkey */ static int default_get_icc_privkey_ref(sc_card_t * card, u8 ** buf, size_t * len) { return SC_ERROR_NOT_SUPPORTED; } /* Retrieve SN.IFD (8 bytes left padded with zeroes if needed) */ static int default_get_sn_ifd(sc_card_t * card) { return SC_ERROR_NOT_SUPPORTED; } /* Retrieve SN.ICC (8 bytes left padded with zeroes if needed) */ static int default_get_sn_icc(sc_card_t * card) { return SC_ERROR_NOT_SUPPORTED; } static cwa_provider_t default_cwa_provider = { /************ data related with SM operations *************************/ /************ operations related with secure channel creation *********/ /* pre and post operations */ default_create_pre_ops, default_create_post_ops, /* Get ICC intermediate CA path */ default_get_icc_intermediate_ca_cert, /* Get ICC certificate path */ default_get_icc_cert, /* Obtain RSA public key from RootCA */ default_get_root_ca_pubkey, /* Obtain RSA IFD private key */ default_get_ifd_privkey, /* Retrieve CVC intermediate CA certificate and length */ default_get_cvc_ca_cert, /* Retrieve CVC IFD certificate and length */ default_get_cvc_ifd_cert, /* Get public key references for Root CA to validate intermediate CA cert */ default_get_root_ca_pubkey_ref, /* Get public key reference for IFD intermediate CA certificate */ default_get_intermediate_ca_pubkey_ref, /* Get public key reference for IFD CVC certificate */ default_get_ifd_pubkey_ref, /* Get ICC private key reference */ default_get_icc_privkey_ref, /* Get IFD Serial Number */ default_get_sn_ifd, /* Get ICC Serial Number */ default_get_sn_icc, }; /** * Get a copy of default cwa provider. * * @param card pointer to card info structure * @return copy of default provider or null on error */ cwa_provider_t *cwa_get_default_provider(sc_card_t * card) { cwa_provider_t *res = NULL; if (!card || !card->ctx) return NULL; LOG_FUNC_CALLED(card->ctx); res = calloc(1, sizeof(cwa_provider_t)); if (!res) { sc_log(card->ctx, "Cannot allocate space for cwa_provider"); return NULL; } memcpy(res, &default_cwa_provider, sizeof(cwa_provider_t)); return res; } /* end of cwa14890.c */ #undef __CWA14890_C__ #endif /* ENABLE_OPENSSL */ OpenSC-0.26.1/src/libopensc/cwa14890.h000066400000000000000000000234661474147347300170560ustar00rootroot00000000000000/** * cwa14890.h: Defines, Typedefs and prototype functions for SM Messaging according CWA-14890 standard. * * 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 * Direccion General de la Policia y de la Guardia Civil * * 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 __CWA14890_H__ #define __CWA14890_H__ #if defined(ENABLE_OPENSSL) && defined(ENABLE_SM) /* Flags for setting SM status */ #define CWA_SM_OFF 0x00 /** Disable SM channel */ #define CWA_SM_ON 0x01 /** Enable SM channel */ /* TAGS for encoded APDU's */ #define CWA_SM_PLAIN_TAG 0x81 /** Plain value (to be protected by CC) */ #define CWA_SM_CRYPTO_TAG 0x87 /** Padding-content + cryptogram */ #define CWA_SM_MAC_TAG 0x8E /** Cryptographic checksum (MAC) */ #define CWA_SM_LE_TAG 0x97 /** Le (to be protected by CC ) */ #define CWA_SM_STATUS_TAG 0x99 /** Processing status (SW1-SW2 mac protected ) */ /*************** data structures for CWA14890 SM handling **************/ #include "libopensc/types.h" #include #include /** * Data and function pointers to provide information to create and handle * Secure Channel. */ typedef struct cwa_provider_st { /************ operations related with secure channel creation *********/ /* pre and post operations */ /** * CWA-14890 SM stablisment pre-operations. * * This code is called before any operation required in * standard cwa14890 SM stablisment process. It's usually * 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 establishment procedure * * @param card pointer to card driver structure * @param provider pointer to SM data provider for DNIe * @return SC_SUCCESS if OK. else error code */ int (*cwa_create_pre_ops) (sc_card_t * card, struct cwa_provider_st * provider); /** * CWA-14890 SM stablisment post-operations. * * 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 * @param provider pointer to SM data provider for DNIe * @return SC_SUCCESS if OK. else error code */ int (*cwa_create_post_ops) (sc_card_t * card, struct cwa_provider_st * provider); /** * Get ICC (card) intermediate CA Certificate. * * @param card Pointer to card driver structure * @param cert where to store resulting certificate * @return SC_SUCCESS if ok; else error code */ int (*cwa_get_icc_intermediate_ca_cert) (sc_card_t * card, X509 ** cert); /** * Get ICC (card) certificate. * * @param card Pointer to card driver structure * @param cert where to store resulting certificate * @return SC_SUCCESS if ok; else error code */ int (*cwa_get_icc_cert) (sc_card_t * card, X509 ** cert); /** * Obtain RSA public key from RootCA. * * @param root_ca_key pointer to resulting returned key * @return SC_SUCCESS if ok; else error code */ int (*cwa_get_root_ca_pubkey) (sc_card_t * card, EVP_PKEY ** key); /** * Get RSA IFD (Terminal) private key data. * * Notice that resulting data should be kept in memory as little * as possible Erasing them once used * * @param card pointer to card driver structure * @param ifd_privkey where to store IFD private key * @return SC_SUCCESS if ok; else error code */ int (*cwa_get_ifd_privkey) (sc_card_t * card, EVP_PKEY ** key); /* TODO: * CVC handling routines should be grouped in just retrieve CVC * certificate. The key reference, as stated by CWA should be * extracted from CVC... * * 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 handling API * become available in standard OpenSSL * *@see http://openpace.sourceforge.net */ /** * Retrieve IFD (application) CVC intermediate CA certificate and length. * * Returns a byte array with the intermediate CA certificate * (in CardVerifiable Certificate format) to be sent to the * card in External Authentication process * * @param card Pointer to card driver Certificate * @param cert Where to store resulting byte array * @param length len of returned byte array * @return SC_SUCCESS if ok; else error code */ int (*cwa_get_cvc_ca_cert) (sc_card_t * card, u8 ** cert, size_t * length); /** * Retrieve IFD (application) CVC certificate and length. * * Returns a byte array with the application's certificate * (in CardVerifiable Certificate format) to be sent to the * card in External Authentication process * * @param card Pointer to card driver Certificate * @param cert Where to store resulting byte array * @param length len of returned byte array * @return SC_SUCCESS if ok; else error code */ int (*cwa_get_cvc_ifd_cert) (sc_card_t * card, u8 ** cert, size_t * length); /** * Retrieve public key reference for Root CA to validate CVC intermediate CA certs. * * This is required in the process of On card external authenticate * @param card Pointer to card driver structure * @param buf where to store resulting key reference * @param len where to store buffer length * @return SC_SUCCESS if ok; else error code */ int (*cwa_get_root_ca_pubkey_ref) (sc_card_t * card, u8 ** buf, size_t * len); /** * Get public key reference for intermediate CA to validate IFD cert. * * This is required in the process of On card external authenticate * * @param card Pointer to card driver structure * @param buf where to store resulting key reference * @param len where to store buffer length * @return SC_SUCCESS if ok; else error code */ int (*cwa_get_intermediate_ca_pubkey_ref) (sc_card_t * card, u8 ** buf, size_t * len); /** * Retrieve public key reference for IFD certificate. * * This tells the card with in memory key reference is to be used * when CVC cert is sent for external auth procedure * * @param card pointer to card driver structure * @param buf where to store data to be sent * @param len where to store data length * @return SC_SUCCESS if ok; else error code */ int (*cwa_get_ifd_pubkey_ref) (sc_card_t * card, u8 ** buf, size_t * len); /** * Retrieve key reference for ICC private key. * * @param card pointer to card driver structure * @param buf where to store data * @param len where to store data length * @return SC_SUCCESS if ok; else error */ int (*cwa_get_icc_privkey_ref) (sc_card_t * card, u8 ** buf, size_t * len); /** * Retrieve SN.IFD - Terminal Serial Number. * * Result SN is 8 bytes long left padded with zeroes if required. * The result should stored in card->sm_ctx.info.session.cwa.ifd.sn * * @param card pointer to card structure * @return SC_SUCCESS if ok; else error */ int (*cwa_get_sn_ifd) (sc_card_t * card); /** * Get SN.ICC - Card Serial Number. * * Result value is 8 bytes long left padded with zeroes if needed) * The result should stored in card->sm_ctx.info.session.cwa.icc.sn * * @param card pointer to card structure * @return SC_SUCCESS if ok; else error */ int (*cwa_get_sn_icc) (sc_card_t * card); } cwa_provider_t; /************************** external function prototypes ******************/ /** * Create Secure channel. * * Based on Several documents: * - "Understanding the DNIe" * - "Manual de comandos del DNIe" * - ISO7816-4 and CWA14890-{1,2} * * @param card card info structure * @param provider pointer to cwa provider * @param flag Requested SM final state (OFF,COLD,WARM) * @return SC_SUCCESS if OK; else error code */ extern int cwa_create_secure_channel(sc_card_t * card, cwa_provider_t * provider, int flag); /** * Decode an APDU response. * * Calling this functions means that It's has been verified * That apdu response comes in TLV encoded format and needs decoding * Based on section 9 of CWA-14890 and Sect 6 of iso7816-4 standards * And DNIe's manual * * @param card card info structure * @param provider cwa provider data to handle SM channel * @param apdu apdu to be decoded * @return SC_SUCCESS if ok; else error code */ extern int cwa_decode_response(sc_card_t * card, cwa_provider_t * provider, sc_apdu_t * apdu); /** * Encode an APDU. * * Calling this functions means that It's has been verified * That source apdu needs encoding * Based on section 9 of CWA-14890 and Sect 6 of iso7816-4 standards * And DNIe's manual * * @param card card info structure * @param provider cwa provider data to handle SM channel * @param from apdu to be encoded * @param to Where to store encoded apdu * @return SC_SUCCESS if ok; else error code */ extern int cwa_encode_apdu(sc_card_t * card, cwa_provider_t * provider, sc_apdu_t * from, sc_apdu_t * to); /** * Gets a default cwa_provider structure. * * @param card Pointer to card driver information * @return default cwa_provider data, or null on error */ extern cwa_provider_t *cwa_get_default_provider(sc_card_t * card); #endif /* ENABLE_OPENSSL */ #endif OpenSC-0.26.1/src/libopensc/dir.c000066400000000000000000000270771474147347300164510ustar00rootroot00000000000000/* * dir.c: Stuff for handling EF(DIR) * * Copyright (C) 2001, 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., 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 "asn1.h" struct app_entry { const u8 *aid; size_t aid_len; const char *desc; }; 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\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[] = { { "aid", SC_ASN1_OCTET_STRING, SC_ASN1_APP | 15, 0, NULL, NULL }, { "label", SC_ASN1_UTF8STRING, SC_ASN1_APP | 16, SC_ASN1_OPTIONAL, NULL, NULL }, { "path", SC_ASN1_OCTET_STRING, SC_ASN1_APP | 17, SC_ASN1_OPTIONAL, NULL, NULL }, { "ddo", SC_ASN1_OCTET_STRING, SC_ASN1_APP | 19 | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; static const struct sc_asn1_entry c_asn1_dir[] = { { "dirRecord", SC_ASN1_STRUCT, SC_ASN1_APP | 1 | SC_ASN1_CONS, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; static int parse_dir_record(sc_card_t *card, u8 ** buf, size_t *buflen, int rec_nr) { struct sc_context *ctx = card->ctx; struct sc_asn1_entry asn1_dirrecord[5], asn1_dir[2]; scconf_block *conf_block = NULL; sc_app_info_t *app = NULL; struct sc_aid aid; u8 label[128], path[128], ddo[128]; 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); sc_format_asn1_entry(asn1_dirrecord + 0, aid.value, &aid.len, 0); sc_format_asn1_entry(asn1_dirrecord + 1, label, &label_len, 0); sc_format_asn1_entry(asn1_dirrecord + 2, path, &path_len, 0); sc_format_asn1_entry(asn1_dirrecord + 3, ddo, &ddo_len, 0); r = sc_asn1_decode(ctx, asn1_dir, *buf, *buflen, (const u8 **) buf, buflen); if (r == SC_ERROR_ASN1_END_OF_CONTENTS) LOG_FUNC_RETURN(ctx, r); LOG_TEST_RET(ctx, r, "EF(DIR) parsing failed"); conf_block = sc_get_conf_block(ctx, "framework", "pkcs15", 1); if (conf_block) { scconf_block **blocks = NULL; char aid_str[SC_MAX_AID_STRING_SIZE]; int ignore_app = 0; 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_bool(blocks[0], "disable", 0)); free(blocks); } if (ignore_app) { sc_log(ctx, "Application '%s' ignored", aid_str); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } } app = calloc(1, sizeof(struct sc_app_info)); if (app == NULL) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); memcpy(&app->aid, &aid, sizeof(struct sc_aid)); if (asn1_dirrecord[1].flags & SC_ASN1_PRESENT) app->label = strdup((char *) label); else app->label = NULL; 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."); } memcpy(app->path.value, path, path_len); app->path.len = path_len; app->path.type = SC_PATH_TYPE_PATH; } else { /* application path not present: use AID as application path */ memcpy(app->path.value, aid.value, aid.len); app->path.len = aid.len; app->path.type = SC_PATH_TYPE_DF_NAME; } 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"); } memcpy(app->ddo.value, ddo, ddo_len); app->ddo.len = ddo_len; } else { app->ddo.value = NULL; app->ddo.len = 0; } app->rec_nr = rec_nr; card->app[card->app_count] = app; card->app_count++; LOG_FUNC_RETURN(ctx, SC_SUCCESS); } int sc_enum_apps(sc_card_t *card) { struct sc_context *ctx = card->ctx; sc_path_t path; int ef_structure; size_t file_size, jj; int r, ii, idx; struct sc_file *ef_dir = NULL; LOG_FUNC_CALLED(ctx); sc_free_apps(card); card->app_count = 0; sc_format_path("3F002F00", &path); 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 (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 = 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; 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) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); p = buf; r = sc_read_binary(card, 0, buf, file_size, 0); if (r < 0) { free(buf); LOG_TEST_RET(ctx, r, "sc_read_binary() failed"); } bufsize = r; while (bufsize > 0) { if (card->app_count == SC_MAX_CARD_APPS) { sc_log(ctx, "Too many applications on card"); break; } r = parse_dir_record(card, &p, &bufsize, -1); if (r) break; } if (buf) free(buf); } else { /* record structure */ unsigned char buf[256], *p; unsigned int rec_nr; size_t rec_size; /* Arbitrary set '16' as maximal number of records to check out: * 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, 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"); if (card->app_count == SC_MAX_CARD_APPS) { sc_log(ctx, "Too many applications on card"); break; } rec_size = r; p = buf; parse_dir_record(card, &p, &rec_size, (int)rec_nr); } } /* Move known PKCS#15 applications to the head of the list */ for (ii=0, idx=0; iiapp_count; ii++) { for (jj=0; jj < sizeof(apps)/sizeof(apps[0]); jj++) { if (apps[jj].aid_len != card->app[ii]->aid.len) continue; if (memcmp(apps[jj].aid, card->app[ii]->aid.value, apps[jj].aid_len)) continue; break; } if (ii != idx && jj < sizeof(apps)/sizeof(apps[0])) { struct sc_app_info *tmp = card->app[idx]; card->app[idx] = card->app[ii]; card->app[ii] = tmp; idx++; } } LOG_FUNC_RETURN(ctx, SC_SUCCESS); } void sc_free_apps(sc_card_t *card) { int i; for (i = 0; i < card->app_count; i++) { free(card->app[i]->label); free(card->app[i]->ddo.value); free(card->app[i]); } card->app_count = -1; } static int encode_dir_record(sc_context_t *ctx, const sc_app_info_t *app, u8 **buf, size_t *buflen) { struct sc_asn1_entry asn1_dirrecord[5], asn1_dir[2]; sc_app_info_t tapp = *app; int r; size_t label_len; 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, 1); sc_format_asn1_entry(asn1_dirrecord + 0, (void *) tapp.aid.value, (void *) &tapp.aid.len, 1); if (tapp.label != NULL) { label_len = strlen(tapp.label); sc_format_asn1_entry(asn1_dirrecord + 1, tapp.label, &label_len, 1); } if (tapp.path.len) sc_format_asn1_entry(asn1_dirrecord + 2, (void *) tapp.path.value, (void *) &tapp.path.len, 1); if (tapp.ddo.value != NULL && tapp.ddo.len) sc_format_asn1_entry(asn1_dirrecord + 3, (void *) tapp.ddo.value, (void *) &tapp.ddo.len, 1); r = sc_asn1_encode(ctx, asn1_dir, buf, buflen); LOG_TEST_RET(ctx, r, "Encode DIR record error"); return SC_SUCCESS; } static int update_transparent(sc_card_t *card, sc_file_t *file) { u8 *rec, *buf = NULL, *tmp; size_t rec_size, buf_size = 0; int i, r; for (i = 0; i < card->app_count; i++) { r = encode_dir_record(card->ctx, card->app[i], &rec, &rec_size); if (r) { if (buf) free(buf); return r; } if (!rec_size) continue; tmp = (u8 *) realloc(buf, buf_size + rec_size); if (!tmp) { if (rec) free(rec); if (buf) free(buf); return SC_ERROR_OUT_OF_MEMORY; } buf = tmp; memcpy(buf + buf_size, rec, rec_size); buf_size += rec_size; free(rec); rec=NULL; } if (file->size > buf_size) { tmp = (u8 *) realloc(buf, file->size); if (!tmp) { free(buf); return SC_ERROR_OUT_OF_MEMORY; } buf = tmp; memset(buf + buf_size, 0, file->size - buf_size); buf_size = file->size; } r = sc_update_binary(card, 0, buf, buf_size, 0); free(buf); LOG_TEST_RET(card->ctx, r, "Unable to update EF(DIR)"); return SC_SUCCESS; } static int update_single_record(sc_card_t *card, sc_app_info_t *app) { u8 *rec; size_t rec_size; int r; r = encode_dir_record(card->ctx, app, &rec, &rec_size); if (r) return r; if (app->rec_nr > 0) 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); if (r == SC_ERROR_NOT_SUPPORTED) { /* if the card doesn't support APPEND RECORD we try a * UPDATE RECORD on the next unused record (and hope * that there is a record with this index). */ int rec_nr = 0, i; for(i = 0; i < card->app_count; i++) 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, 0, rec, rec_size, SC_RECORD_BY_REC_NR); } } else { sc_log(card->ctx, "invalid record number\n"); r = SC_ERROR_INTERNAL; } free(rec); LOG_TEST_RET(card->ctx, r, "Unable to update EF(DIR) record"); return 0; } static int update_records(sc_card_t *card) { int i, r; for (i = 0; i < card->app_count; i++) { r = update_single_record(card, card->app[i]); if (r) return r; } return 0; } int sc_update_dir(sc_card_t *card, sc_app_info_t *app) { sc_path_t path; sc_file_t *file; int r; sc_format_path("3F002F00", &path); r = sc_select_file(card, &path, &file); LOG_TEST_RET(card->ctx, r, "unable to select EF(DIR)"); if (file->ef_structure == SC_FILE_EF_TRANSPARENT) r = update_transparent(card, file); else if (app == NULL) r = update_records(card); else r = update_single_record(card, app); sc_file_free(file); return r; } OpenSC-0.26.1/src/libopensc/ef-atr.c000066400000000000000000000137371474147347300170470ustar00rootroot00000000000000/* * ef-atr.c: Stuff for handling EF(ATR) * * Copyright (C) 2001, 2002 Juha Yrjölä * Copyright (C) 2010 Viktor Tarasov * OpenTrust * * 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 #include #include #include "internal.h" #include "asn1.h" #include "iso7816.h" static int sc_parse_ef_atr_content(struct sc_card *card, unsigned char *buf, size_t buflen) { struct sc_context *ctx = card->ctx; const unsigned char *tag = NULL; size_t taglen; struct sc_ef_atr ef_atr; LOG_FUNC_CALLED(ctx); memset(&ef_atr, 0, sizeof(struct sc_ef_atr)); tag = sc_asn1_find_tag(ctx, buf, buflen, ISO7816_TAG_II_CARD_SERVICE, &taglen); if (tag && taglen >= 1) { ef_atr.card_service = *tag; sc_log(ctx, "EF.ATR: card service 0x%X", ef_atr.card_service); } tag = sc_asn1_find_tag(ctx, buf, buflen, ISO7816_TAG_II_PRE_ISSUING, &taglen); if (tag) { size_t len = taglen > sizeof(ef_atr.pre_issuing) ? sizeof(ef_atr.pre_issuing) : taglen; memcpy(ef_atr.pre_issuing, tag, len); ef_atr.pre_issuing_len = len; sc_log(ctx, "EF.ATR: Pre-Issuing data '%s'", sc_dump_hex(ef_atr.pre_issuing, ef_atr.pre_issuing_len)); } tag = sc_asn1_find_tag(ctx, buf, buflen, ISO7816_TAG_II_CARD_CAPABILITIES, &taglen); if (tag && taglen >= 3) { ef_atr.df_selection = *(tag + 0); ef_atr.unit_size = *(tag + 1); ef_atr.card_capabilities = *(tag + 2); sc_log(ctx, "EF.ATR: DF selection %X, unit_size %"SC_FORMAT_LEN_SIZE_T"X, card caps %X", ef_atr.df_selection, ef_atr.unit_size, ef_atr.card_capabilities); } if (ef_atr.card_capabilities & ISO7816_CAP_EXTENDED_LENGTH_INFO) { /* Extended Length Information in EF.ATR/INFO */ tag = sc_asn1_find_tag(ctx, buf, buflen, ISO7816_TAG_II_EXTENDED_LENGTH, &taglen); if (tag && taglen >= 8) { /* The command- and response-APDU size limitations are defined by * two integers, each nested in a DO'02'. * We skip parsing the nested DOs and jump directly to the numbers */ ef_atr.max_command_apdu = bebytes2ushort(tag + 2); ef_atr.max_response_apdu = bebytes2ushort(tag + 6); sc_log(ctx, "EF.ATR: Biggest command APDU %"SC_FORMAT_LEN_SIZE_T"u bytes, response APDU %"SC_FORMAT_LEN_SIZE_T"u", ef_atr.max_command_apdu, ef_atr.max_response_apdu); } } tag = sc_asn1_find_tag(ctx, buf, buflen, ISO7816_TAG_II_AID, &taglen); if (tag) { if (taglen > sizeof(ef_atr.aid.value)) LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Invalid MF AID size"); memcpy(ef_atr.aid.value, tag, taglen); ef_atr.aid.len = taglen; sc_log(ctx, "EF.ATR: AID '%s'", sc_dump_hex(ef_atr.aid.value, ef_atr.aid.len)); } /* IAS/ECC specific issuer data: contains the max send/recv buffer sizes in plain and SM modes */ tag = sc_asn1_find_tag(ctx, buf, buflen, IASECC_TAG_II_IO_BUFFER_SIZES, &taglen); if (tag) { size_t len = taglen > sizeof(ef_atr.issuer_data) ? sizeof(ef_atr.issuer_data) : taglen; memcpy(ef_atr.issuer_data, tag, len); ef_atr.issuer_data_len = len; sc_log(ctx, "EF.ATR: Issuer data '%s'", sc_dump_hex(ef_atr.issuer_data, ef_atr.issuer_data_len)); } tag = sc_asn1_find_tag(ctx, buf, buflen, ISO7816_TAG_II_ALLOCATION_SCHEME, &taglen); if (tag) { sc_log(ctx, "EF.ATR: DER encoded OID %s", sc_dump_hex(tag, taglen)); tag = sc_asn1_find_tag(ctx, tag, taglen, SC_ASN1_TAG_OBJECT, &taglen); if (tag) { sc_log(ctx, "EF.ATR: OID %s", sc_dump_hex(tag, taglen)); if(sc_asn1_decode_object_id(tag, taglen, &ef_atr.allocation_oid)) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ASN1_OBJECT); } } tag = sc_asn1_find_tag(ctx, buf, buflen, ISO7816_TAG_II_STATUS_SW, &taglen); if (tag && taglen == 2) { ef_atr.status = *(tag + 0) * 0x100 + *(tag + 1); sc_log(ctx, "EF.ATR: status word 0x%X", ef_atr.status); } 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, &ef_atr, sizeof(struct sc_ef_atr)); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } int sc_parse_ef_atr(struct sc_card *card) { struct sc_context *ctx = card->ctx; struct sc_path path; 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_GOTO_ERR(ctx, rv, "Cannot select EF(ATR) file"); if (file->size) { size = file->size; } else { size = 1024; } buf = malloc(size); 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_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"); rv = SC_SUCCESS; err: sc_file_free(file); free(buf); LOG_FUNC_RETURN(ctx, rv); } void sc_free_ef_atr(sc_card_t *card) { if (card->ef_atr) free(card->ef_atr); card->ef_atr = NULL; } OpenSC-0.26.1/src/libopensc/ef-gdo.c000066400000000000000000000057231474147347300170260ustar00rootroot00000000000000/* * 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); } OpenSC-0.26.1/src/libopensc/errors.c000066400000000000000000000124011474147347300171700ustar00rootroot00000000000000/* * errors.c: The textual representation of errors * * Copyright (C) 2001, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "errors.h" #define DIM(v) (sizeof(v)/(sizeof((v)[0]))) const char *sc_strerror(int error) { unsigned int error_index = 0; const char *rdr_errors[] = { "Generic reader error", "No readers found", "UNUSED", "UNUSED", "Card not present", "Card removed", "Card reset", "Transmit failed", "Timed out while waiting for user input", "Input operation cancelled by user", "The two PINs did not match", "Message too long (keypad)", "Timeout while waiting for event from card reader", "Unresponsive card (correctly inserted?)", "Reader detached", "Reader reattached", "Reader in use by another application" }; const unsigned int rdr_base = -SC_ERROR_READER; const char *card_errors[] = { "Card command failed", "File not found", "Record not found", "Unsupported CLA byte in APDU", "Unsupported INS byte in APDU", "Incorrect parameters in APDU", "Wrong length", "Card memory failure", "Card does not support the requested operation", "Not allowed", "Card is invalid or cannot be handled", "Security status not satisfied", "Authentication method blocked", "Unknown data received from card", "PIN code or key incorrect", "File already exists", "Data object not found", "Not enough memory on card", "Part of returned data may be corrupted", "End of file/record reached before reading Le bytes", "Reference data not usable" }; const unsigned int card_base = -SC_ERROR_CARD_CMD_FAILED; const char *arg_errors[] = { "Invalid arguments", "UNUSED", "UNUSED", "Buffer too small", "Invalid PIN length", "Invalid data", }; const unsigned int arg_base = -SC_ERROR_INVALID_ARGUMENTS; const char *int_errors[] = { "Internal error", "Invalid ASN.1 object", "Required ASN.1 object not found", "Premature end of ASN.1 stream", "Out of memory", "Too many objects", "Object not valid", "Requested object not found", "Not supported", "Passphrase required", "Inconsistent configuration", "Decryption failed", "Wrong padding", "Unsupported card", "Unable to load external module", "EF offset too large", "Not implemented", "Invalid Simple TLV object", "Premature end of Simple TLV stream", }; const unsigned int int_base = -SC_ERROR_INTERNAL; const char *p15i_errors[] = { "Generic PKCS#15 initialization error", "Syntax error", "Inconsistent or incomplete PKCS#15 profile", "Key length/algorithm not supported by card", "No default (transport) key available", "Non unique object ID", "Unable to load key and certificate(s) from file", "UNUSED", "File template not found", "Invalid PIN reference", "File too small", }; const unsigned int p15i_base = -SC_ERROR_PKCS15INIT; const char *sm_errors[] = { "Generic Secure Messaging error", "Data enciphering error", "Invalid secure messaging level", "No session keys", "Invalid session keys", "Secure Messaging not initialized", "Cannot authenticate card", "Random generation error", "Secure messaging keyset not found", "IFD data missing", "SM not applied", "SM session already active", "Invalid checksum" }; const unsigned int sm_base = -SC_ERROR_SM; const char *misc_errors[] = { "Unknown error", "PKCS#15 compatible smart card not found", }; const unsigned int misc_base = -SC_ERROR_UNKNOWN; const char *no_errors = "Success"; const char **errors = NULL; unsigned int count = 0, err_base = 0; if (!error) return no_errors; error_index = error < 0 ? (unsigned)(-(long long int)error) : (unsigned)error; if (error_index >= misc_base) { errors = misc_errors; count = DIM(misc_errors); err_base = misc_base; } else if (error_index >= sm_base) { errors = sm_errors; count = DIM(sm_errors); err_base = sm_base; } else if (error_index >= p15i_base) { errors = p15i_errors; count = DIM(p15i_errors); err_base = p15i_base; } else if (error_index >= int_base) { errors = int_errors; count = DIM(int_errors); err_base = int_base; } else if (error_index >= arg_base) { errors = arg_errors; count = DIM(arg_errors); err_base = arg_base; } else if (error_index >= card_base) { errors = card_errors; count = DIM(card_errors); err_base = card_base; } else if (error_index >= rdr_base) { errors = rdr_errors; count = DIM(rdr_errors); err_base = rdr_base; } error_index -= err_base; if (error_index >= count) return misc_errors[0]; return errors[error_index]; } OpenSC-0.26.1/src/libopensc/errors.h000066400000000000000000000115441474147347300172040ustar00rootroot00000000000000/* * errors.h: OpenSC error codes * * Copyright (C) 2001, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _OPENSC_ERRORS_H #define _OPENSC_ERRORS_H #ifdef __cplusplus extern "C" { #endif #define SC_SUCCESS 0 /* Errors related to reader operation */ #define SC_ERROR_READER -1100 #define SC_ERROR_NO_READERS_FOUND -1101 /* Unused: -1102 */ /* Unused: -1103 */ #define SC_ERROR_CARD_NOT_PRESENT -1104 #define SC_ERROR_CARD_REMOVED -1105 #define SC_ERROR_CARD_RESET -1106 #define SC_ERROR_TRANSMIT_FAILED -1107 #define SC_ERROR_KEYPAD_TIMEOUT -1108 #define SC_ERROR_KEYPAD_CANCELLED -1109 #define SC_ERROR_KEYPAD_PIN_MISMATCH -1110 #define SC_ERROR_KEYPAD_MSG_TOO_LONG -1111 #define SC_ERROR_EVENT_TIMEOUT -1112 #define SC_ERROR_CARD_UNRESPONSIVE -1113 #define SC_ERROR_READER_DETACHED -1114 #define SC_ERROR_READER_REATTACHED -1115 #define SC_ERROR_READER_LOCKED -1116 /* Resulting from a card command or related to the card*/ #define SC_ERROR_CARD_CMD_FAILED -1200 #define SC_ERROR_FILE_NOT_FOUND -1201 #define SC_ERROR_RECORD_NOT_FOUND -1202 #define SC_ERROR_CLASS_NOT_SUPPORTED -1203 #define SC_ERROR_INS_NOT_SUPPORTED -1204 #define SC_ERROR_INCORRECT_PARAMETERS -1205 #define SC_ERROR_WRONG_LENGTH -1206 #define SC_ERROR_MEMORY_FAILURE -1207 #define SC_ERROR_NO_CARD_SUPPORT -1208 #define SC_ERROR_NOT_ALLOWED -1209 #define SC_ERROR_INVALID_CARD -1210 #define SC_ERROR_SECURITY_STATUS_NOT_SATISFIED -1211 #define SC_ERROR_AUTH_METHOD_BLOCKED -1212 #define SC_ERROR_UNKNOWN_DATA_RECEIVED -1213 #define SC_ERROR_PIN_CODE_INCORRECT -1214 #define SC_ERROR_FILE_ALREADY_EXISTS -1215 #define SC_ERROR_DATA_OBJECT_NOT_FOUND -1216 #define SC_ERROR_NOT_ENOUGH_MEMORY -1217 #define SC_ERROR_CORRUPTED_DATA -1218 #define SC_ERROR_FILE_END_REACHED -1219 #define SC_ERROR_REF_DATA_NOT_USABLE -1220 /* Returned by OpenSC library when called with invalid arguments */ #define SC_ERROR_INVALID_ARGUMENTS -1300 /* Unused: -1301 */ /* Unused: -1302 */ #define SC_ERROR_BUFFER_TOO_SMALL -1303 #define SC_ERROR_INVALID_PIN_LENGTH -1304 #define SC_ERROR_INVALID_DATA -1305 /* Resulting from OpenSC internal operation */ #define SC_ERROR_INTERNAL -1400 #define SC_ERROR_INVALID_ASN1_OBJECT -1401 #define SC_ERROR_ASN1_OBJECT_NOT_FOUND -1402 #define SC_ERROR_ASN1_END_OF_CONTENTS -1403 #define SC_ERROR_OUT_OF_MEMORY -1404 #define SC_ERROR_TOO_MANY_OBJECTS -1405 #define SC_ERROR_OBJECT_NOT_VALID -1406 #define SC_ERROR_OBJECT_NOT_FOUND -1407 #define SC_ERROR_NOT_SUPPORTED -1408 #define SC_ERROR_PASSPHRASE_REQUIRED -1409 #define SC_ERROR_INCONSISTENT_CONFIGURATION -1410 #define SC_ERROR_DECRYPT_FAILED -1411 #define SC_ERROR_WRONG_PADDING -1412 #define SC_ERROR_WRONG_CARD -1413 #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 #define SC_ERROR_SYNTAX_ERROR -1501 #define SC_ERROR_INCONSISTENT_PROFILE -1502 #define SC_ERROR_INCOMPATIBLE_KEY -1503 #define SC_ERROR_NO_DEFAULT_KEY -1504 #define SC_ERROR_NON_UNIQUE_ID -1505 #define SC_ERROR_CANNOT_LOAD_KEY -1506 /* Unused: -1007 */ #define SC_ERROR_TEMPLATE_NOT_FOUND -1508 #define SC_ERROR_INVALID_PIN_REFERENCE -1509 #define SC_ERROR_FILE_TOO_SMALL -1510 /* Related to secure messaging */ #define SC_ERROR_SM -1600 #define SC_ERROR_SM_ENCRYPT_FAILED -1601 #define SC_ERROR_SM_INVALID_LEVEL -1602 #define SC_ERROR_SM_NO_SESSION_KEYS -1603 #define SC_ERROR_SM_INVALID_SESSION_KEY -1604 #define SC_ERROR_SM_NOT_INITIALIZED -1605 #define SC_ERROR_SM_AUTHENTICATION_FAILED -1606 #define SC_ERROR_SM_RAND_FAILED -1607 #define SC_ERROR_SM_KEYSET_NOT_FOUND -1608 #define SC_ERROR_SM_IFD_DATA_MISSING -1609 #define SC_ERROR_SM_NOT_APPLIED -1610 #define SC_ERROR_SM_SESSION_ALREADY_ACTIVE -1611 #define SC_ERROR_SM_INVALID_CHECKSUM -1612 /* Errors that do not fit the categories above */ #define SC_ERROR_UNKNOWN -1900 #define SC_ERROR_PKCS15_APP_NOT_FOUND -1901 const char *sc_strerror(int sc_errno); #ifdef __cplusplus } #endif #endif OpenSC-0.26.1/src/libopensc/gp.c000066400000000000000000000062041474147347300162660ustar00rootroot00000000000000/* * 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) { struct sc_apdu apdu; int rv; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 0x04, 0x0C); apdu.lc = aid->len; apdu.data = aid->value; apdu.datalen = aid->len; rv = sc_transmit_apdu(card, &apdu); if (rv < 0) return rv; rv = sc_check_sw(card, apdu.sw1, apdu.sw2); if (rv < 0) return rv; return (int)apdu.resplen; } /* 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); } OpenSC-0.26.1/src/libopensc/gp.h000066400000000000000000000041671474147347300163010ustar00rootroot00000000000000/* * 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 OpenSC-0.26.1/src/libopensc/iasecc-sdo.c000066400000000000000000001215171474147347300176770ustar00rootroot00000000000000/* * iasecc-sdo.c: library to manipulate the Security Data Objects (SDO) * used by IAS/ECC card support. * * Copyright (C) 2010 Viktor Tarasov * OpenTrust * * 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 #ifdef ENABLE_OPENSSL /* empty file without openssl */ #include #include #include "internal.h" #include "asn1.h" #include "cardctl.h" #include "iasecc.h" #include "iasecc-sdo.h" static int iasecc_parse_size(unsigned char *data, size_t data_len, size_t *out); static int 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; size_t offs; unsigned char mask = 0x40; if (flags) acls = &docp->acls_contactless; if (!acls->size) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); 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 (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], docp->scbs[4],docp->scbs[5],docp->scbs[6]); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } int iasecc_sdo_convert_acl(struct sc_card *card, struct iasecc_sdo *sdo, unsigned char op, unsigned *out_method, unsigned *out_ref) { struct sc_context *ctx = card->ctx; struct acl_op { unsigned char op; unsigned char mask; } ops[] = { {SC_AC_OP_PSO_COMPUTE_SIGNATURE,IASECC_ACL_PSO_SIGNATURE}, {SC_AC_OP_INTERNAL_AUTHENTICATE,IASECC_ACL_INTERNAL_AUTHENTICATE}, {SC_AC_OP_PSO_DECRYPT, IASECC_ACL_PSO_DECIPHER}, {SC_AC_OP_GENERATE, IASECC_ACL_GENERATE_KEY}, {SC_AC_OP_UPDATE, IASECC_ACL_PUT_DATA}, {SC_AC_OP_READ, IASECC_ACL_GET_DATA}, {0x00, 0x00} }; unsigned char mask = 0x80, op_mask = 0; int ii; LOG_FUNC_CALLED(ctx); for (ii=0; ops[ii].mask; ii++) { if (op == ops[ii].op) { op_mask = ops[ii].mask; break; } } if (op_mask == 0) LOG_FUNC_RETURN(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); 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)); if (!sdo->docp.amb && sdo->docp.acls_contact.size) { int rv = iasecc_parse_acls(card, &sdo->docp, 0); LOG_TEST_RET(ctx, rv, "Cannot parse ACLs in DOCP"); } *out_method = SC_AC_NEVER; *out_ref = SC_AC_NEVER; for (ii=0; ii<7; ii++) { mask >>= 1; if (sdo->docp.amb & mask) { if (op_mask == mask) { unsigned char scb = sdo->docp.scbs[ii]; sc_log(ctx, "ii:%i, scb:0x%X", ii, scb); *out_ref = scb & 0x0F; if (scb == 0) *out_method = SC_AC_NONE; else if (scb == 0xFF) *out_method = SC_AC_NEVER; else if ((scb & IASECC_SCB_METHOD_MASK) == IASECC_SCB_METHOD_USER_AUTH) *out_method = SC_AC_SEN; else if ((scb & IASECC_SCB_METHOD_MASK) == IASECC_SCB_METHOD_EXT_AUTH) *out_method = SC_AC_AUT; else if ((scb & IASECC_SCB_METHOD_MASK) == IASECC_SCB_METHOD_SM) *out_method = SC_AC_PRO; else *out_method = SC_AC_SCB, *out_ref = scb; break; } } } sc_log(ctx, "returns method %X; ref %X", *out_method, *out_ref); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } void iasecc_sdo_free_fields(struct sc_card *card, struct iasecc_sdo *sdo) { 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) { 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) { 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) { 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)); } void iasecc_sdo_free(struct sc_card *card, struct iasecc_sdo *sdo) { iasecc_sdo_free_fields(card, sdo); free(sdo); } static int 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; int ii, offs, len, parsed_len = -1; 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); } else if (*(data + offs) == IASECC_CRT_TAG_REFERENCE) { int nn_refs = sizeof(crt.refs) / sizeof(crt.refs[0]); for (ii=0; iicrts[ii].tag) break; if (ii==SC_MAX_CRTS_IN_SE) LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "iasecc_crt_parse() error: too much CRTs in SE"); memcpy(&se->crts[ii], &crt, sizeof(crt)); parsed_len = len + 2; LOG_FUNC_RETURN(ctx, parsed_len); } int iasecc_se_get_crt(struct sc_card *card, struct iasecc_se_info *se, struct sc_crt *crt) { struct sc_context *ctx = card->ctx; int ii; LOG_FUNC_CALLED(ctx); if (!se || !crt) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); sc_log(ctx, "CRT search template: %X:%X:%X, refs %X:%X:...", crt->tag, crt->algo, crt->usage, crt->refs[0], crt->refs[1]); for (ii=0; iicrts[ii].tag; ii++) { if (crt->tag != se->crts[ii].tag) continue; if (crt->algo && crt->algo != se->crts[ii].algo) continue; if (crt->usage && crt->usage != se->crts[ii].usage) continue; if (crt->refs[0] && crt->refs[0] != se->crts[ii].refs[0]) continue; memcpy(crt, &se->crts[ii], sizeof(*crt)); sc_log(ctx, "iasecc_se_get_crt() found CRT with refs %X:%X:...", se->crts[ii].refs[0], se->crts[ii].refs[1]); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } sc_log(ctx, "iasecc_se_get_crt() CRT is not found"); return SC_ERROR_DATA_OBJECT_NOT_FOUND; } int iasecc_se_get_crt_by_usage(struct sc_card *card, struct iasecc_se_info *se, unsigned char tag, unsigned char usage, struct sc_crt *crt) { struct sc_context *ctx = card->ctx; int ii; LOG_FUNC_CALLED(ctx); if (!se || !crt || !tag || !usage) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); sc_log(ctx, "CRT search template with TAG:0x%X and UQB:0x%X", tag, usage); for (ii=0; iicrts[ii].tag; ii++) { if (tag != se->crts[ii].tag) continue; if (usage != se->crts[ii].usage) continue; memcpy(crt, &se->crts[ii], sizeof(*crt)); sc_log(ctx, "iasecc_se_get_crt() found CRT with refs %X:%X:...", crt->refs[0], crt->refs[1]); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } sc_log(ctx, "iasecc_se_get_crt() CRT is not found"); LOG_FUNC_RETURN(ctx, SC_ERROR_DATA_OBJECT_NOT_FOUND); } 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; int size_size; int rv; LOG_FUNC_CALLED(ctx); 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, data_len - 1, &size); LOG_TEST_RET(ctx, size_size, "parse error: invalid size data of IASECC_SDO_TEMPLATE"); if (data_len - 1 < size) 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 %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, 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: 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 %d", size, size_size); } if (*data != IASECC_SDO_CLASS_SE) { sc_log(ctx, "Invalid SE tag 0x%X; data length %"SC_FORMAT_LEN_SIZE_T"u", *data, data_len); LOG_FUNC_RETURN(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); } 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: invalid SE data size"); offs = 1 + size_size; for (; offs < data_len;) { 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 totally parsed"); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int iasecc_parse_size(unsigned char *data, size_t data_len, size_t *out) { if (data_len > 0 && *data < 0x80) { *out = *data; return 1; } else if (data_len > 1 && *data == 0x81) { *out = *(data + 1); return 2; } else if (data_len > 2 && *data == 0x82) { *out = *(data + 1) * 0x100 + *(data + 2); return 3; } return SC_ERROR_INVALID_DATA; } static int 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; 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; } else { tlv->tag = *data; tag_len = 1; } sc_log(ctx, "iasecc_parse_get_tlv() tlv->tag 0x%X", tlv->tag); 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) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); memcpy(tlv->value, data + size_len + tag_len, tlv->size); tlv->on_card = 1; sc_log(ctx, "iasecc_parse_get_tlv() parsed %"SC_FORMAT_LEN_SIZE_T"u bytes", tag_len + size_len + tlv->size); return (int)(tag_len + size_len + tlv->size); } static int iasecc_parse_chv(struct sc_card *card, unsigned char *data, size_t data_len, struct iasecc_sdo_chv *chv) { struct sc_context *ctx = card->ctx; size_t offs = 0; int rv; LOG_FUNC_CALLED(ctx); while(offs < data_len) { struct iasecc_extended_tlv 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) { free(chv->size_max.value); chv->size_max = tlv; } 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) { free(chv->value.value); chv->value = tlv; } else { free(tlv.value); LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "parse error: non CHV SDO tag"); } offs += rv; } LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int iasecc_parse_prvkey(struct sc_card *card, unsigned char *data, size_t data_len, struct iasecc_sdo_prvkey *prvkey) { struct sc_context *ctx = card->ctx; size_t offs = 0; int rv; LOG_FUNC_CALLED(ctx); while(offs < data_len) { struct iasecc_extended_tlv 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) { free(prvkey->compulsory.value); prvkey->compulsory = tlv; } else { free(tlv.value); LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "parse error: non PrvKey SDO tag"); } offs += rv; } LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int iasecc_parse_pubkey(struct sc_card *card, unsigned char *data, size_t data_len, struct iasecc_sdo_pubkey *pubkey) { struct sc_context *ctx = card->ctx; size_t offs = 0; int rv; LOG_FUNC_CALLED(ctx); while(offs < data_len) { struct iasecc_extended_tlv 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) { free(pubkey->n.value); pubkey->n = tlv; } 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) { free(pubkey->chr.value); pubkey->chr = tlv; } 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) { free(pubkey->compulsory.value); pubkey->compulsory = tlv; } else { free(tlv.value); LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "parse error: non PubKey SDO tag"); } offs += rv; } LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int iasecc_parse_keyset(struct sc_card *card, unsigned char *data, size_t data_len, struct iasecc_sdo_keyset *keyset) { struct sc_context *ctx = card->ctx; size_t offs = 0; int rv; LOG_FUNC_CALLED(ctx); while(offs < data_len) { struct iasecc_extended_tlv 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) { free(keyset->compulsory.value); keyset->compulsory = tlv; } else { free(tlv.value); LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "parse error: non KeySet SDO tag"); } offs += rv; } LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int iasecc_parse_docp(struct sc_card *card, unsigned char *data, size_t data_len, struct iasecc_sdo *sdo) { struct sc_context *ctx = card->ctx; size_t offs = 0; int rv; LOG_FUNC_CALLED(ctx); while(offs < data_len) { struct iasecc_extended_tlv 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 returned %i; tag %X; size %"SC_FORMAT_LEN_SIZE_T"u", rv, tlv.tag, tlv.size); if (tlv.tag == IASECC_DOCP_TAG_ACLS) { int _rv = iasecc_parse_docp(card, tlv.value, tlv.size, sdo); free(tlv.value); 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_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"); } offs += rv; } rv = iasecc_parse_acls(card, &sdo->docp, 0); LOG_TEST_RET(ctx, rv, "Cannot parse ACLs in DOCP"); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int 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; int tlv_size, rv; 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, 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); if (tlv.tag == IASECC_DOCP_TAG) { sc_log(ctx, "iasecc_sdo_parse_data() parse IASECC_DOCP_TAG: 0x%X; size %"SC_FORMAT_LEN_SIZE_T"u", tlv.tag, tlv.size); rv = iasecc_parse_docp(card, tlv.value, tlv.size, sdo); sc_log(ctx, "iasecc_sdo_parse_data() parsed IASECC_DOCP_TAG rv %i", rv); free(tlv.value); LOG_TEST_RET(ctx, rv, "parse error: cannot parse DOCP"); } 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) { 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); 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) { 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); 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) { 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); 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) { 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); 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); } return tlv_size; } 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; 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, data_len - 1, &size); LOG_TEST_RET(ctx, size_size, "parse error: invalid size data of IASECC_SDO_TEMPLATE"); data += size_size + 1; data_len = size; sc_log(ctx, "IASECC_SDO_TEMPLATE: size %"SC_FORMAT_LEN_SIZE_T"u, size_size %d", size, size_size); } if (*data != IASECC_SDO_TAG_HEADER) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); if (sdo->sdo_class != (*(data + 1) & 0x7F)) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); if (sdo->sdo_ref != (*(data + 2) & 0x3F)) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); 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: invalid SDO data size"); sc_log(ctx, "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, 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 totally parsed"); sc_log(ctx, "docp.acls_contact.size %"SC_FORMAT_LEN_SIZE_T"u, docp.size.size %"SC_FORMAT_LEN_SIZE_T"u", sdo->docp.acls_contact.size, sdo->docp.size.size); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } int iasecc_sdo_allocate_and_parse(struct sc_card *card, unsigned char *data, size_t data_len, struct iasecc_sdo **out) { struct sc_context *ctx = card->ctx; struct iasecc_sdo *sdo = NULL; 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) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); sdo = calloc(1, sizeof(struct iasecc_sdo)); if (!sdo) return SC_ERROR_OUT_OF_MEMORY; *out = sdo; sdo->sdo_class = *(data + 1) & 0x7F; sdo->sdo_ref = *(data + 2) & 0x3F; sc_log(ctx, "sdo_class 0x%X, sdo_ref 0x%X", sdo->sdo_class, sdo->sdo_ref); if (data_len == 3) LOG_FUNC_RETURN(ctx, SC_SUCCESS); 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: invalid SDO data size"); sc_log(ctx, "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, 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 totally parsed"); sc_log(ctx, "docp.acls_contact.size %"SC_FORMAT_LEN_SIZE_T"u; docp.size.size %"SC_FORMAT_LEN_SIZE_T"u", sdo->docp.acls_contact.size, sdo->docp.size.size); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int iasecc_update_blob(struct sc_context *ctx, struct iasecc_extended_tlv *tlv, unsigned char **blob, size_t *blob_size) { unsigned char *pp = NULL; size_t offs = 0, sz; if (tlv->size == 0) LOG_FUNC_RETURN(ctx, SC_SUCCESS); sz = tlv->size + 2; if (tlv->tag > 0xFF) sz += 1; if (tlv->size > 0x7F && tlv->size < 0x100) sz += 1; else if (tlv->size >= 0x100) sz += 2; pp = realloc(*blob, *blob_size + sz); if (!pp) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); if (tlv->tag > 0xFF) *(pp + *blob_size + offs++) = (tlv->tag >> 8) & 0xFF; *(pp + *blob_size + offs++) = tlv->tag & 0xFF; if (tlv->size >= 0x100) { *(pp + *blob_size + offs++) = 0x82; *(pp + *blob_size + offs++) = (tlv->size >> 8) & 0xFF; } else if (tlv->size > 0x7F) { *(pp + *blob_size + offs++) = 0x81; } *(pp + *blob_size + offs++) = tlv->size & 0xFF; memcpy(pp + *blob_size + offs, tlv->value, tlv->size); *blob_size += sz; *blob = pp; return 0; } 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 = NULL, *tmp_blob = NULL, *docp_blob = NULL; size_t blob_size; int rv; LOG_FUNC_CALLED(ctx); if (!docp->acls_contact.size || (docp->size.size != 2)) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); memset(&tlv, 0, sizeof(tlv)); memset(&tlv_st, 0, sizeof(tlv_st)); blob_size = 0; rv = iasecc_update_blob(ctx, &docp->acls_contact, &st_blob, &blob_size); 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_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; blob_size = 0; rv = iasecc_update_blob(ctx, &tlv, &tmp_blob, &blob_size); 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_GOTO_ERR(ctx, rv, "ECC: cannot add NAME to blob"); rv = iasecc_update_blob(ctx, &docp->tries_maximum, &tmp_blob, &blob_size); 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_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_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_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_GOTO_ERR(ctx, rv, "ECC: cannot add NON REPUDIATION to blob"); rv = iasecc_update_blob(ctx, &docp->size, &tmp_blob, &blob_size); 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_GOTO_ERR(ctx, rv, "ECC: cannot add IDATA to blob"); tlv.tag = IASECC_DOCP_TAG; tlv.size = blob_size; tlv.value = tmp_blob; blob_size = 0; rv = iasecc_update_blob(ctx, &tlv, &docp_blob, &blob_size); 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); } static unsigned iasecc_sdo_encode_asn1_tag(unsigned in_tag) { unsigned short_tag; unsigned out_tag; for (short_tag = in_tag; short_tag > 0xFF; short_tag >>= 8) ; out_tag = in_tag; switch (short_tag & SC_ASN1_TAG_CLASS) { case SC_ASN1_TAG_APPLICATION: out_tag |= SC_ASN1_APP; break; case SC_ASN1_TAG_CONTEXT: out_tag |= SC_ASN1_CTX; break; case SC_ASN1_TAG_PRIVATE: out_tag |= SC_ASN1_PRV; break; } return out_tag; } int iasecc_sdo_encode_create(struct sc_context *ctx, struct iasecc_sdo *sdo, unsigned char **out) { struct sc_asn1_entry c_asn1_docp_data[2] = { { "docpData", SC_ASN1_OCTET_STRING, 0, SC_ASN1_ALLOC, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; struct sc_asn1_entry c_asn1_create_data[2] = { { "createData", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_APP | SC_ASN1_CONS, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; struct sc_asn1_entry asn1_docp_data[2], asn1_create_data[2]; unsigned char *blob = NULL; size_t len, out_len; unsigned sdo_full_ref; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "ecc_sdo_encode_create() sdo->sdo_class %X", sdo->sdo_class); sc_log(ctx, "id %02X%02X%02X", IASECC_SDO_TAG_HEADER, sdo->sdo_class | 0x80, sdo->sdo_ref); if (out) *out = NULL; rv = iasecc_encode_docp(ctx, &sdo->docp, &blob, &len); LOG_TEST_RET(ctx, rv, "ECC encode DOCP error"); sdo_full_ref = (sdo->sdo_ref&0x3F) + 0x100*(sdo->sdo_class | IASECC_OBJECT_REF_LOCAL) + 0x10000*IASECC_SDO_TAG_HEADER; c_asn1_docp_data[0].tag = iasecc_sdo_encode_asn1_tag(sdo_full_ref) | SC_ASN1_CONS; sc_copy_asn1_entry(c_asn1_docp_data, asn1_docp_data); sc_copy_asn1_entry(c_asn1_create_data, asn1_create_data); sc_format_asn1_entry(asn1_docp_data + 0, blob, &len, 1); sc_format_asn1_entry(asn1_create_data + 0, asn1_docp_data, NULL, 1); rv = sc_asn1_encode(ctx, asn1_create_data, out, &out_len); LOG_TEST_RET(ctx, rv, "Encode create data error"); if (out) sc_debug(ctx, SC_LOG_DEBUG_ASN1,"Create data: %s", sc_dump_hex(*out, out_len)); LOG_FUNC_RETURN(ctx, (int)out_len); } int iasecc_sdo_encode_update_field(struct sc_context *ctx, unsigned char sdo_class, unsigned char sdo_ref, struct iasecc_extended_tlv *tlv, unsigned char **out) { unsigned sdo_full_ref; size_t out_len; int rv; struct sc_asn1_entry c_asn1_field_value[2] = { { "fieldValue", SC_ASN1_OCTET_STRING, 0, SC_ASN1_ALLOC, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; struct sc_asn1_entry c_asn1_sdo_field[2] = { { "sdoField", SC_ASN1_STRUCT, 0, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; struct sc_asn1_entry c_asn1_class_data[2] = { { "classData", SC_ASN1_STRUCT, 0, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; struct sc_asn1_entry c_asn1_update_data[2] = { { "updateData", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_APP | SC_ASN1_CONS, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; struct sc_asn1_entry asn1_field_value[4], asn1_sdo_field[2], asn1_class_data[2], asn1_update_data[2]; LOG_FUNC_CALLED(ctx); c_asn1_field_value[0].tag = iasecc_sdo_encode_asn1_tag(tlv->tag); c_asn1_sdo_field[0].tag = iasecc_sdo_encode_asn1_tag(tlv->parent_tag) | SC_ASN1_CONS; sdo_full_ref = (sdo_ref&0x3F) + 0x100*(sdo_class | IASECC_OBJECT_REF_LOCAL) + 0x10000*IASECC_SDO_TAG_HEADER; c_asn1_class_data[0].tag = iasecc_sdo_encode_asn1_tag(sdo_full_ref) | SC_ASN1_CONS; sc_copy_asn1_entry(c_asn1_field_value, asn1_field_value); sc_copy_asn1_entry(c_asn1_sdo_field, asn1_sdo_field); sc_copy_asn1_entry(c_asn1_class_data, asn1_class_data); sc_copy_asn1_entry(c_asn1_update_data, asn1_update_data); sc_format_asn1_entry(asn1_field_value + 0, tlv->value, &tlv->size, 1); sc_format_asn1_entry(asn1_sdo_field + 0, asn1_field_value, NULL, 1); sc_format_asn1_entry(asn1_class_data + 0, asn1_sdo_field, NULL, 1); sc_format_asn1_entry(asn1_update_data + 0, asn1_class_data, NULL, 1); rv = sc_asn1_encode(ctx, asn1_update_data, out, &out_len); LOG_TEST_RET(ctx, rv, "Encode update data error"); 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, (int)out_len); } int iasecc_sdo_encode_rsa_update(struct sc_context *ctx, struct iasecc_sdo *sdo, struct sc_pkcs15_prkey_rsa *rsa, struct iasecc_sdo_update *sdo_update) { LOG_FUNC_CALLED(ctx); 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; 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) LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "need all private RSA key components"); sdo_update->magic = SC_CARDCTL_IASECC_SDO_MAGIC_PUT_DATA; sdo_update->sdo_ref = sdo->sdo_ref; 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++; sc_log(ctx, "prv_key.compulsory.on_card %i", sdo->data.prv_key.compulsory.on_card); if (!sdo->data.prv_key.compulsory.on_card) { if (sdo->data.prv_key.compulsory.value) { 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++; } } } else if (sdo->sdo_class == IASECC_SDO_CLASS_RSA_PUBLIC) { int indx = 0; sc_log(ctx, "iasecc_sdo_encode_rsa_update(IASECC_SDO_CLASS_RSA_PUBLIC)"); sdo_update->magic = SC_CARDCTL_IASECC_SDO_MAGIC_PUT_DATA; sdo_update->sdo_ref = sdo->sdo_ref; 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++; } 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++; } 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++; } 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++; } /* 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++; } } } else { LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); } LOG_FUNC_RETURN(ctx, SC_SUCCESS); } 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; size_t size = 0, size_size, offs; LOG_FUNC_CALLED(ctx); if (!data || !data_len || !out) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); memset(out, 0, sizeof(*out)); for (offs=0; offs sizeof(out->data)) 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) 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); memcpy(out->ticket, data + offs, 4); offs += 4; have_status = 1; } else if (*(data + offs) == IASECC_CARD_ANSWER_TAG_MAC ) { if (*(data + offs + 1) != 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); memcpy(out->ticket + 4, data + offs, 10); offs += 10; have_mac = 1; } else { LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "iasecc_sm_decode_answer() invalid card answer tag"); } } if (!have_mac || !have_status) LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "iasecc_sm_decode_answer() absent MAC or SW "); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int iasecc_tlv_copy(struct sc_context *ctx, struct iasecc_extended_tlv *in, struct iasecc_extended_tlv *out) { if (!in || !out) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); memset(out, 0, sizeof(struct iasecc_extended_tlv)); out->tag = in->tag; out->parent_tag = in->parent_tag; out->on_card = in->on_card; if (in->value && in->size) { out->value = calloc(1, in->size); if (!out->value) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); memcpy(out->value, in->value, in->size); out->size = in->size; } return SC_SUCCESS; } int iasecc_docp_copy(struct sc_context *ctx, struct iasecc_sdo_docp *in, struct iasecc_sdo_docp *out) { int rv; LOG_FUNC_CALLED(ctx); if (!in || !out) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); memset(out, 0, sizeof(struct iasecc_sdo_docp)); rv = iasecc_tlv_copy(ctx, &in->name, &out->name); LOG_TEST_RET(ctx, rv, "TLV copy error"); rv = iasecc_tlv_copy(ctx, &in->tries_maximum, &out->tries_maximum); LOG_TEST_RET(ctx, rv, "TLV copy error"); rv = iasecc_tlv_copy(ctx, &in->tries_remaining, &out->tries_remaining); LOG_TEST_RET(ctx, rv, "TLV copy error"); rv = iasecc_tlv_copy(ctx, &in->usage_maximum, &out->usage_maximum); LOG_TEST_RET(ctx, rv, "TLV copy error"); rv = iasecc_tlv_copy(ctx, &in->usage_remaining, &out->usage_remaining); LOG_TEST_RET(ctx, rv, "TLV copy error"); rv = iasecc_tlv_copy(ctx, &in->non_repudiation, &out->non_repudiation); LOG_TEST_RET(ctx, rv, "TLV copy error"); rv = iasecc_tlv_copy(ctx, &in->size, &out->size); LOG_TEST_RET(ctx, rv, "TLV copy error"); rv = iasecc_tlv_copy(ctx, &in->acls_contact, &out->acls_contact); LOG_TEST_RET(ctx, rv, "TLV copy error"); rv = iasecc_tlv_copy(ctx, &in->acls_contactless, &out->acls_contactless); LOG_TEST_RET(ctx, rv, "TLV copy error"); out->amb = in->amb; memcpy(out->scbs, in->scbs, sizeof(out->scbs)); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } #else /* we need to define the functions below to export them */ #include "errors.h" int iasecc_sdo_encode_update_field() { return SC_ERROR_NOT_SUPPORTED; } int iasecc_se_get_crt() { return SC_ERROR_NOT_SUPPORTED; } #endif /* ENABLE_OPENSSL */ OpenSC-0.26.1/src/libopensc/iasecc-sdo.h000066400000000000000000000251321474147347300177000ustar00rootroot00000000000000/* * iasecc-sdo.h: Support for IAS/ECC smart cards * * Copyright (C) 2010 Viktor Tarasov * OpenTrust * * 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_IASECC_SDO_H #define SC_IASECC_SDO_H #include "libopensc/types.h" #define IASECC_SDO_TAG_HEADER 0xBF #define IASECC_SDO_TEMPLATE_TAG 0x70 #define IASECC_DOCP_TAG 0xA0 #define IASECC_DOCP_TAG_NAME 0x84 #define IASECC_DOCP_TAG_TRIES_MAXIMUM 0x9A #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_REPUDIATION 0x9E #define IASECC_DOCP_TAG_SIZE 0x80 #define IASECC_DOCP_TAG_ACLS 0xA1 #define IASECC_DOCP_TAG_ACLS_CONTACT 0x8C #define IASECC_DOCP_TAG_ACLS_CONTACTLESS 0x9C #define IASECC_DOCP_TAG_ISSUER_DATA_BER 0xA5 #define IASECC_DOCP_TAG_ISSUER_DATA 0x85 #define IASECC_ACLS_CHV_CHANGE 0 #define IASECC_ACLS_CHV_VERIFY 1 #define IASECC_ACLS_CHV_RESET 2 #define IASECC_ACLS_CHV_PUT_DATA 5 #define IASECC_ACLS_CHV_GET_DATA 6 #define IASECC_ACLS_RSAKEY_PSO_SIGN 0 #define IASECC_ACLS_RSAKEY_INTERNAL_AUTH 1 #define IASECC_ACLS_RSAKEY_PSO_DECIPHER 2 #define IASECC_ACLS_RSAKEY_GENERATE 3 #define IASECC_ACLS_RSAKEY_PUT_DATA 5 #define IASECC_ACLS_RSAKEY_GET_DATA 6 #define IASECC_ACLS_KEYSET_EXTERNAL_AUTH 1 #define IASECC_ACLS_KEYSET_MUTUAL_AUTH 3 #define IASECC_ACLS_KEYSET_PUT_DATA 5 #define IASECC_ACLS_KEYSET_GET_DATA 6 #define IASECC_SDO_CHV_TAG 0x7F41 #define IASECC_SDO_CHV_TAG_SIZE_MAX 0x80 #define IASECC_SDO_CHV_TAG_SIZE_MIN 0x81 #define IASECC_SDO_CHV_TAG_VALUE 0x82 #define IASECC_SDO_PRVKEY_TAG 0x7F48 #define IASECC_SDO_PRVKEY_TAG_P 0x92 #define IASECC_SDO_PRVKEY_TAG_Q 0x93 #define IASECC_SDO_PRVKEY_TAG_IQMP 0x94 #define IASECC_SDO_PRVKEY_TAG_DMP1 0x95 #define IASECC_SDO_PRVKEY_TAG_DMQ1 0x96 #define IASECC_SDO_PRVKEY_TAG_COMPULSORY 0x80 #define IASECC_SDO_PUBKEY_TAG 0x7F49 #define IASECC_SDO_PUBKEY_TAG_N 0x81 #define IASECC_SDO_PUBKEY_TAG_E 0x82 #define IASECC_SDO_PUBKEY_TAG_COMPULSORY 0x80 #define IASECC_SDO_PUBKEY_TAG_CHR 0x5F20 #define IASECC_SDO_PUBKEY_TAG_CHA 0x5F4C #define IASECC_SDO_KEYSET_TAG 0xA2 #define IASECC_SDO_KEYSET_TAG_MAC 0x90 #define IASECC_SDO_KEYSET_TAG_ENC 0x91 #define IASECC_SDO_KEYSET_TAG_COMPULSORY 0x80 #define IASECC_SCB_METHOD_NEED_ALL 0x80 #define IASECC_SCB_METHOD_MASK 0x70 #define IASECC_SCB_METHOD_MASK_REF 0x0F #define IASECC_SCB_METHOD_SM 0x40 #define IASECC_SCB_METHOD_EXT_AUTH 0x20 #define IASECC_SCB_METHOD_USER_AUTH 0x10 #define IASECC_SCB_NEVER 0xFF #define IASECC_SCB_ALWAYS 0x00 #define IASECC_SDO_CLASS_CHV 0x01 #define IASECC_SDO_CLASS_KEYSET 0x0A #define IASECC_SDO_CLASS_RSA_PRIVATE 0x10 #define IASECC_SDO_CLASS_RSA_PUBLIC 0x20 #define IASECC_SDO_CLASS_SE 0x7B #define IASECC_CRT_TAG_AT 0xA4 #define IASECC_CRT_TAG_CT 0xB8 #define IASECC_CRT_TAG_CCT 0xB4 #define IASECC_CRT_TAG_DST 0xB6 #define IASECC_CRT_TAG_HT 0xAA #define IASECC_CRT_TAG_KAT 0xA6 #define IASECC_CRT_TAG_USAGE 0x95 #define IASECC_CRT_TAG_REFERENCE 0x83 #define IASECC_CRT_TAG_ALGO 0x80 #define IASECC_ALGORITHM_SYMMETRIC 0x0C #define IASECC_ALGORITHM_DH 0x0B #define IASECC_ALGORITHM_RSA_PKCS 0x02 #define IASECC_ALGORITHM_RSA_9796_2 0x01 #define IASECC_ALGORITHM_RSA_PKCS_DECRYPT 0x0A #define IASECC_ALGORITHM_SHA1 0x10 #define IASECC_ALGORITHM_SHA2 0x40 #define IASECC_ALGORITHM_ROLE_AUTH 0x1C #define IASECC_ALGORITHM_SYMMETRIC_SHA1 0x0C #define IASECC_ALGORITHM_SYMMETRIC_SHA256 0x8C #define IASECC_UQB_AT_MUTUAL_AUTHENTICATION 0xC0 #define IASECC_UQB_AT_EXTERNAL_AUTHENTICATION 0x80 #define IASECC_UQB_AT_AUTHENTICATION 0x40 #define IASECC_UQB_AT_USER_PASSWORD 0x08 #define IASECC_UQB_AT_USER_BIOMETRIC 0x04 #define IASECC_UQB_DST_VERIFICATION 0x80 #define IASECC_UQB_DST_COMPUTATION 0x40 #define IASECC_UQB_CT_ENCIPHERMENT 0x80 #define IASECC_UQB_CT_DECIPHERMENT 0x40 #define IASECC_UQB_CT_SM_RESPONSE 0x20 #define IASECC_UQB_CT_SM_COMMAND 0x10 #define IASECC_UQB_CCT_VERIFICATION 0x80 #define IASECC_UQB_CCT_COMPUTATION 0x40 #define IASECC_UQB_CCT_SM_RESPONSE 0x20 #define IASECC_UQB_CCT_SM_COMMAND 0x10 #define IASECC_UQB_KAT 0x80 #define IASECC_ACL_GET_DATA 0x01 #define IASECC_ACL_PUT_DATA 0x02 #define IASECC_ACL_GENERATE_KEY 0x08 #define IASECC_ACL_PSO_DECIPHER 0x10 #define IASECC_ACL_INTERNAL_AUTHENTICATE 0x20 #define IASECC_ACL_PSO_SIGNATURE 0x40 #define IASECC_SDO_TAGS_UPDATE_MAX 16 //#define IASECC_SE_CRTS_MAX 24 #define _MAKE_IASECC_SDO_MAGIC(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | ((d))) #define IASECC_SDO_MAGIC _MAKE_IASECC_SDO_MAGIC('E', 'C', 'S', 'D') #define IASECC_SDO_MAGIC_UPDATE _MAKE_IASECC_SDO_MAGIC('E', 'C', 'U', 'D') #define IASECC_SDO_MAGIC_UPDATE_RSA _MAKE_IASECC_SDO_MAGIC('E', 'C', 'U', 'R') #define IASECC_MAX_SCBS 7 //#define IASECC_MAX_CRTS_IN_SE 24 struct iasecc_extended_tlv { unsigned tag; unsigned parent_tag; unsigned char *value; size_t size; unsigned on_card; }; struct iasecc_sdo_docp { struct iasecc_extended_tlv name; struct iasecc_extended_tlv tries_maximum; struct iasecc_extended_tlv tries_remaining; struct iasecc_extended_tlv usage_maximum; struct iasecc_extended_tlv usage_remaining; struct iasecc_extended_tlv non_repudiation; struct iasecc_extended_tlv size; struct iasecc_extended_tlv acls_contact; struct iasecc_extended_tlv acls_contactless; struct iasecc_extended_tlv issuer_data; unsigned char amb, scbs[IASECC_MAX_SCBS]; }; struct iasecc_sdo_chv { struct iasecc_extended_tlv size_max; struct iasecc_extended_tlv size_min; struct iasecc_extended_tlv value; }; struct iasecc_sdo_prvkey { struct iasecc_extended_tlv p; struct iasecc_extended_tlv q; struct iasecc_extended_tlv iqmp; struct iasecc_extended_tlv dmp1; struct iasecc_extended_tlv dmq1; struct iasecc_extended_tlv compulsory; }; struct iasecc_sdo_pubkey { struct iasecc_extended_tlv n; struct iasecc_extended_tlv e; struct iasecc_extended_tlv compulsory; struct iasecc_extended_tlv chr; struct iasecc_extended_tlv cha; }; struct iasecc_sdo_keyset { struct iasecc_extended_tlv mac; struct iasecc_extended_tlv enc; struct iasecc_extended_tlv compulsory; }; struct iasecc_sdo { unsigned char sdo_class; unsigned char sdo_ref; unsigned int usage; struct iasecc_sdo_docp docp; union { struct iasecc_sdo_chv chv; struct iasecc_sdo_prvkey prv_key; struct iasecc_sdo_pubkey pub_key; struct iasecc_sdo_keyset keyset; } data; unsigned not_on_card; unsigned magic; }; struct iasecc_sdo_update { unsigned char sdo_class; unsigned char sdo_ref; struct iasecc_extended_tlv fields[IASECC_SDO_TAGS_UPDATE_MAX]; unsigned magic; }; struct iasecc_sdo_rsa_update { struct iasecc_sdo *sdo_prv_key; struct iasecc_sdo *sdo_pub_key; struct sc_pkcs15_prkey_rsa *p15_rsa; struct iasecc_sdo_update update_prv; struct iasecc_sdo_update update_pub; unsigned magic; }; struct iasecc_se_info { struct iasecc_sdo_docp docp; int reference; struct sc_crt crts[SC_MAX_CRTS_IN_SE]; struct sc_file *df; struct iasecc_se_info *next; unsigned magic; }; struct iasecc_sm_card_answer { unsigned char data[SC_MAX_APDU_BUFFER_SIZE]; size_t data_len; unsigned sw; unsigned char mac[8]; unsigned char ticket[14]; }; struct iasecc_ctl_get_free_reference { size_t key_size; unsigned usage; unsigned access; int index; }; enum IASECC_KEY_TYPE { IASECC_SDO_CLASS_RSA_PRV = 0x10, IASECC_SDO_CLASS_RSA_PUB = 0x20 }; struct iasecc_sm_cmd_update_binary { const unsigned char *data; size_t offs, count; }; struct iasecc_sm_cmd_create_file { const unsigned char *data; size_t size; }; struct sc_card; int iasecc_sdo_convert_acl(struct sc_card *, struct iasecc_sdo *, unsigned char, unsigned *, unsigned *); void iasecc_sdo_free_fields(struct sc_card *, struct iasecc_sdo *); void iasecc_sdo_free(struct sc_card *, struct iasecc_sdo *); int iasecc_se_parse(struct sc_card *, unsigned char *, size_t, struct iasecc_se_info *); int iasecc_sdo_parse(struct sc_card *, unsigned char *, size_t, struct iasecc_sdo *); int iasecc_sdo_allocate_and_parse(struct sc_card *, unsigned char *, size_t, struct iasecc_sdo **); int iasecc_encode_size(size_t, unsigned char *); int iasecc_sdo_encode_create(struct sc_context*, struct iasecc_sdo *, unsigned char **); int iasecc_sdo_encode_update_field(struct sc_context *, unsigned char, unsigned char, struct iasecc_extended_tlv *, unsigned char **); int iasecc_se_get_crt(struct sc_card *, struct iasecc_se_info *, struct sc_crt *); int iasecc_se_get_crt_by_usage(struct sc_card *, struct iasecc_se_info *, unsigned char, unsigned char, struct sc_crt *); int iasecc_sdo_encode_rsa_update(struct sc_context *, struct iasecc_sdo *, struct sc_pkcs15_prkey_rsa *, struct iasecc_sdo_update *); int iasecc_sdo_parse_card_answer(struct sc_context *, unsigned char *, size_t, struct iasecc_sm_card_answer *); int iasecc_docp_copy(struct sc_context *, struct iasecc_sdo_docp *, struct iasecc_sdo_docp *); 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_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); int iasecc_sm_create_file(struct sc_card *card, unsigned se_num, unsigned char *fcp, size_t fcp_len); int iasecc_sm_delete_file(struct sc_card *card, unsigned se_num, unsigned int file_id); int iasecc_sm_rsa_generate(struct sc_card *card, unsigned se_num, struct iasecc_sdo *sdo); int iasecc_sm_rsa_update(struct sc_card *card, unsigned se_num, struct iasecc_sdo_rsa_update *udata); int iasecc_sm_sdo_update(struct sc_card *card, unsigned se_num, struct iasecc_sdo_update *update); #endif OpenSC-0.26.1/src/libopensc/iasecc-sm.c000066400000000000000000000520631474147347300175300ustar00rootroot00000000000000/* * iasecc.h Support for IAS/ECC smart cards * * Copyright (C) 2010 Viktor Tarasov * OpenTrust * * 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 #include "internal.h" #include "asn1.h" #include "cardctl.h" #include "common/compat_strlcpy.h" #include "sm.h" #include "iasecc.h" #include "authentic.h" #ifdef ENABLE_SM static int sm_save_sc_context (struct sc_card *card, struct sm_info *sm_info) { struct sc_context *ctx; struct sc_card_cache *cache; if (!card || !sm_info) return SC_ERROR_INVALID_ARGUMENTS; ctx = card->ctx; cache = &card->cache; sc_log(ctx, "SM save context: cache(valid:%i,current_df:%p)", cache->valid, cache->current_df); if (cache->valid && cache->current_df) { sm_info->current_path_df = cache->current_df->path; if (cache->current_df->path.type == SC_PATH_TYPE_DF_NAME) { if (cache->current_df->path.aid.len) { sm_info->current_aid = cache->current_df->path.aid; } else { memcpy(sm_info->current_aid.value, cache->current_df->path.value, cache->current_df->path.len); sm_info->current_aid.len = cache->current_df->path.len; } } } if (cache->valid && cache->current_ef) sm_info->current_path_ef = cache->current_ef->path; return SC_SUCCESS; } static int sm_restore_sc_context(struct sc_card *card, struct sm_info *sm_info) { int rv = SC_SUCCESS; if (sm_info->current_path_df.type == SC_PATH_TYPE_DF_NAME && sm_info->current_path_df.len) rv = sc_select_file(card, &sm_info->current_path_df, NULL); if (sm_info->current_path_ef.len && rv == SC_SUCCESS) rv = sc_select_file(card, &sm_info->current_path_ef, NULL); memset(&sm_info->current_path_df, 0, sizeof(sm_info->current_path_df)); memset(&sm_info->current_path_ef, 0, sizeof(sm_info->current_path_ef)); return rv; } static int iasecc_sm_transmit_apdus(struct sc_card *card, struct sc_remote_data *rdata, unsigned char *out, size_t *out_len) { struct sc_context *ctx = card->ctx; struct sc_remote_apdu *rapdu = rdata->data; int rv = SC_SUCCESS; size_t offs = 0; LOG_FUNC_CALLED(ctx); sc_log(ctx, "iasecc_sm_transmit_apdus() rdata-length %i", rdata->length); while (rapdu) { sc_log(ctx, "iasecc_sm_transmit_apdus() rAPDU flags 0x%lX", rapdu->apdu.flags); rv = sc_transmit_apdu(card, &rapdu->apdu); LOG_TEST_RET(ctx, rv, "iasecc_sm_transmit_apdus() failed to execute r-APDU"); rv = sc_check_sw(card, rapdu->apdu.sw1, rapdu->apdu.sw2); if (rv < 0 && !(rapdu->flags & SC_REMOTE_APDU_FLAG_NOT_FATAL)) LOG_TEST_RET(ctx, rv, "iasecc_sm_transmit_apdus() fatal error %i"); if (out && out_len && (rapdu->flags & SC_REMOTE_APDU_FLAG_RETURN_ANSWER)) { size_t len = rapdu->apdu.resplen > (*out_len - offs) ? (*out_len - offs) : rapdu->apdu.resplen; memcpy(out + offs, rapdu->apdu.resp, len); offs += len; /* TODO: decode and gather data answers */ } rapdu = rapdu->next; } if (out_len) *out_len = offs; LOG_FUNC_RETURN(ctx, rv); } /* Big TODO: do SM release in all handles, clean the saved card context -- current DF, EF, etc. */ static int sm_release (struct sc_card *card, struct sc_remote_data *rdata, unsigned char *out, size_t out_len) { struct sc_context *ctx = card->ctx; struct sm_info *sm_info = &card->sm_ctx.info; int rv; LOG_FUNC_CALLED(ctx); if (!card->sm_ctx.module.ops.finalize) LOG_FUNC_RETURN(ctx, SC_SUCCESS); rv = card->sm_ctx.module.ops.finalize(ctx, sm_info, rdata, out, out_len); sm_restore_sc_context(card, sm_info); LOG_FUNC_RETURN(ctx, rv); } #endif int iasecc_sm_external_authentication(struct sc_card *card, unsigned skey_ref, int *tries_left) { struct sc_context *ctx = card->ctx; #ifdef ENABLE_SM struct sm_info *sm_info = &card->sm_ctx.info; struct sm_cwa_session *cwa_session = &sm_info->session.cwa; struct sc_remote_data rdata; struct sc_apdu apdu; unsigned char sbuf[0x100]; int rv, offs; LOG_FUNC_CALLED(ctx); sc_log(ctx, "iasecc_sm_external_authentication(): SKey ref %i", skey_ref); if (card->sm_ctx.sm_mode == SM_MODE_NONE) LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Cannot do 'External Authentication' without SM activated "); strlcpy(sm_info->config_section, card->sm_ctx.config_section, sizeof(sm_info->config_section)); sm_info->cmd = SM_CMD_EXTERNAL_AUTH; sm_info->serialnr = card->serialnr; sm_info->card_type = card->type; sm_info->sm_type = SM_TYPE_CWA14890; cwa_session->params.crt_at.usage = IASECC_UQB_AT_EXTERNAL_AUTHENTICATION; cwa_session->params.crt_at.algo = IASECC_ALGORITHM_ROLE_AUTH; cwa_session->params.crt_at.refs[0] = skey_ref; offs = 0; sbuf[offs++] = IASECC_CRT_TAG_ALGO; sbuf[offs++] = 0x01; sbuf[offs++] = IASECC_ALGORITHM_ROLE_AUTH; sbuf[offs++] = IASECC_CRT_TAG_REFERENCE; sbuf[offs++] = 0x01; sbuf[offs++] = skey_ref; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x81, 0xA4); apdu.data = sbuf; apdu.datalen = offs; apdu.lc = offs; rv = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(ctx, rv, "iasecc_sm_external_authentication(): APDU transmit failed"); rv = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(ctx, rv, "iasecc_sm_external_authentication(): set SE error"); rv = sc_get_challenge(card, cwa_session->card_challenge, sizeof(cwa_session->card_challenge)); LOG_TEST_RET(ctx, rv, "iasecc_sm_external_authentication(): set SE error"); sc_remote_data_init(&rdata); if (!card->sm_ctx.module.ops.initialize) LOG_TEST_RET(ctx, SC_ERROR_SM_NOT_INITIALIZED, "No SM module"); rv = card->sm_ctx.module.ops.initialize(ctx, sm_info, &rdata); LOG_TEST_RET(ctx, rv, "SM: INITIALIZE failed"); sc_log(ctx, "sm_iasecc_external_authentication(): rdata length %i\n", rdata.length); rv = iasecc_sm_transmit_apdus (card, &rdata, NULL, 0); if (rv == SC_ERROR_PIN_CODE_INCORRECT && tries_left) *tries_left = (rdata.data + rdata.length - 1)->apdu.sw2 & 0x0F; LOG_TEST_RET(ctx, rv, "sm_iasecc_external_authentication(): execute failed"); LOG_FUNC_RETURN(ctx, rv); #else LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "built without support of SM and External Authentication"); return SC_ERROR_NOT_SUPPORTED; #endif } #ifdef ENABLE_SM static int iasecc_sm_se_mutual_authentication(struct sc_card *card, unsigned se_num) { struct sc_context *ctx = card->ctx; struct sm_info *sm_info = &card->sm_ctx.info; struct iasecc_se_info se; struct sc_crt *crt = &sm_info->session.cwa.params.crt_at; struct sc_apdu apdu; unsigned char sbuf[0x100]; int rv, offs; memset(&se, 0, sizeof(se)); se.reference = se_num; crt->usage = IASECC_UQB_AT_MUTUAL_AUTHENTICATION; crt->tag = IASECC_CRT_TAG_AT; rv = iasecc_se_get_info(card, &se); 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); /* MSE SET Mutual Authentication SK scheme */ offs = 0; sbuf[offs++] = IASECC_CRT_TAG_ALGO; sbuf[offs++] = 0x01; sbuf[offs++] = crt->algo; sbuf[offs++] = IASECC_CRT_TAG_REFERENCE; sbuf[offs++] = 0x01; sbuf[offs++] = crt->refs[0]; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0xC1, 0xA4); apdu.data = sbuf; apdu.datalen = offs; apdu.lc = offs; rv = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(ctx, rv, "SM set SE mutual auth.: APDU transmit failed"); rv = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(ctx, rv, "SM set SE mutual auth.: set SE error"); LOG_FUNC_RETURN(ctx, rv); } #endif int iasecc_sm_initialize(struct sc_card *card, unsigned se_num, unsigned cmd) { struct sc_context *ctx = card->ctx; #ifdef ENABLE_SM struct sm_info *sm_info = &card->sm_ctx.info; struct sm_cwa_session *cwa_session = &sm_info->session.cwa; struct sc_remote_data rdata; int rv; LOG_FUNC_CALLED(ctx); strlcpy(sm_info->config_section, card->sm_ctx.config_section, sizeof(sm_info->config_section)); sm_info->cmd = cmd; sm_info->serialnr = card->serialnr; sm_info->card_type = card->type; sm_info->sm_type = SM_TYPE_CWA14890; rv = iasecc_sm_se_mutual_authentication(card, se_num); LOG_TEST_RET(ctx, rv, "iasecc_sm_initialize() MUTUAL AUTHENTICATION failed"); 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); rv = sm_save_sc_context(card, sm_info); LOG_TEST_RET(ctx, rv, "iasecc_sm_initialize() cannot save current context"); if (!card->sm_ctx.module.ops.initialize) LOG_TEST_RET(ctx, SC_ERROR_SM_NOT_INITIALIZED, "iasecc_sm_initialize() no SM module"); rv = card->sm_ctx.module.ops.initialize(ctx, sm_info, &rdata); LOG_TEST_RET(ctx, rv, "iasecc_sm_initialize() INITIALIZE failed"); if (rdata.length == 1) { rdata.data->flags |= SC_REMOTE_APDU_FLAG_RETURN_ANSWER; rdata.data->apdu.flags &= ~SC_APDU_FLAGS_NO_GET_RESP; } else { LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "TODO: SM init with more then one APDU"); } cwa_session->mdata_len = sizeof(cwa_session->mdata); 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() transmit APDUs failed"); rdata.free(&rdata); sc_log(ctx, "MA data(len:%"SC_FORMAT_LEN_SIZE_T"u) '%s'", cwa_session->mdata_len, sc_dump_hex(cwa_session->mdata, cwa_session->mdata_len)); if (cwa_session->mdata_len != 0x48) LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "iasecc_sm_initialize() invalid MUTUAL AUTHENTICATE result data"); LOG_FUNC_RETURN(ctx, SC_SUCCESS); #else LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "built without support of Secure-Messaging"); return SC_ERROR_NOT_SUPPORTED; #endif } #ifdef ENABLE_SM static int iasecc_sm_cmd(struct sc_card *card, struct sc_remote_data *rdata) { #define AUTH_SM_APDUS_MAX 12 #define ENCODED_APDUS_MAX_LENGTH (AUTH_SM_APDUS_MAX * (SC_MAX_APDU_BUFFER_SIZE * 2 + 64) + 32) struct sc_context *ctx = card->ctx; struct sm_info *sm_info = &card->sm_ctx.info; struct sm_cwa_session *session = &sm_info->session.cwa; struct sc_remote_apdu *rapdu = NULL; int rv; LOG_FUNC_CALLED(ctx); if (!card->sm_ctx.module.ops.get_apdus) LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); rv = card->sm_ctx.module.ops.get_apdus(ctx, sm_info, session->mdata, session->mdata_len, rdata); LOG_TEST_RET(ctx, rv, "iasecc_sm_cmd() 'GET APDUS' failed"); sc_log(ctx, "iasecc_sm_cmd() %i remote APDUs to transmit", rdata->length); for (rapdu = rdata->data; rapdu; rapdu = rapdu->next) { struct sc_apdu *apdu = &rapdu->apdu; sc_log(ctx, "iasecc_sm_cmd() apdu->ins:0x%X, resplen %"SC_FORMAT_LEN_SIZE_T"u", apdu->ins, apdu->resplen); if (!apdu->ins) break; rv = sc_transmit_apdu(card, apdu); if (rv < 0) { sc_log(ctx, "iasecc_sm_cmd() APDU transmit error rv:%i", rv); break; } rv = sc_check_sw(card, apdu->sw1, apdu->sw2); if (rv < 0 && !(rapdu->flags & SC_REMOTE_APDU_FLAG_NOT_FATAL)) { sc_log(ctx, "iasecc_sm_cmd() APDU error rv:%i", rv); break; } sc_log(ctx, "iasecc_sm_cmd() apdu->resplen %"SC_FORMAT_LEN_SIZE_T"u", apdu->resplen); } LOG_FUNC_RETURN(ctx, rv); } #endif int iasecc_sm_rsa_generate(struct sc_card *card, unsigned se_num, struct iasecc_sdo *sdo) { struct sc_context *ctx = card->ctx; #ifdef ENABLE_SM struct sm_info *sm_info = &card->sm_ctx.info; struct sc_remote_data rdata; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "iasecc_sm_rsa_generate() SE#%i, SDO(class:%X,ref:%X)", se_num, sdo->sdo_class, sdo->sdo_ref); rv = iasecc_sm_initialize(card, se_num, SM_CMD_RSA_GENERATE); LOG_TEST_RET(ctx, rv, "iasecc_sm_rsa_generate() SM initialize failed"); sm_info->cmd_data = sdo; sc_remote_data_init(&rdata); rv = iasecc_sm_cmd(card, &rdata); LOG_TEST_RET(ctx, rv, "iasecc_sm_rsa_generate() SM cmd failed"); rv = sm_release (card, &rdata, NULL, 0); LOG_TEST_RET(ctx, rv, "iasecc_sm_rsa_generate() SM release failed"); rdata.free(&rdata); LOG_FUNC_RETURN(ctx, rv); #else LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "built without support of Secure-Messaging"); return SC_ERROR_NOT_SUPPORTED; #endif } int iasecc_sm_rsa_update(struct sc_card *card, unsigned se_num, struct iasecc_sdo_rsa_update *udata) { struct sc_context *ctx = card->ctx; #ifdef ENABLE_SM struct sm_info *sm_info = &card->sm_ctx.info; struct sc_remote_data rdata; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "SM update RSA: SE#: 0x%X, SDO(class:0x%X:ref:%X)", se_num, udata->sdo_prv_key->sdo_class, udata->sdo_prv_key->sdo_ref); rv = iasecc_sm_initialize(card, se_num, SM_CMD_RSA_UPDATE); LOG_TEST_RET(ctx, rv, "iasecc_sm_rsa_update() SM initialize failed"); sm_info->cmd_data = udata; sc_remote_data_init(&rdata); rv = iasecc_sm_cmd(card, &rdata); LOG_TEST_RET(ctx, rv, "iasecc_sm_rsa_update() SM cmd failed"); rv = sm_release (card, &rdata, NULL, 0); LOG_TEST_RET(ctx, rv, "iasecc_sm_rsa_update() SM release failed"); rdata.free(&rdata); LOG_FUNC_RETURN(ctx, rv); #else LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "built without support of Secure-Messaging"); return SC_ERROR_NOT_SUPPORTED; #endif } int iasecc_sm_pin_verify(struct sc_card *card, unsigned se_num, struct sc_pin_cmd_data *data, int *tries_left) { struct sc_context *ctx = card->ctx; #ifdef ENABLE_SM struct sm_info *sm_info = &card->sm_ctx.info; struct sc_remote_data rdata; int rv; LOG_FUNC_CALLED(ctx); 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"); sm_info->cmd_data = data; sc_remote_data_init(&rdata); rv = iasecc_sm_cmd(card, &rdata); if (rv && rdata.length && tries_left) if (rdata.data->apdu.sw1 == 0x63 && (rdata.data->apdu.sw2 & 0xF0) == 0xC0) *tries_left = rdata.data->apdu.sw2 & 0x0F; LOG_TEST_RET(ctx, rv, "iasecc_sm_pin_verify() SM 'PIN VERIFY' failed"); rv = sm_release (card, &rdata, NULL, 0); LOG_TEST_RET(ctx, rv, "iasecc_sm_pin_verify() SM release failed"); rdata.free(&rdata); LOG_FUNC_RETURN(ctx, rv); #else LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "built without support of Secure-Messaging"); return SC_ERROR_NOT_SUPPORTED; #endif } int iasecc_sm_sdo_update(struct sc_card *card, unsigned se_num, struct iasecc_sdo_update *update) { struct sc_context *ctx = card->ctx; #ifdef ENABLE_SM struct sm_info *sm_info = &card->sm_ctx.info; struct sc_remote_data rdata; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "iasecc_sm_sdo_update() SE#%i, SDO(class:0x%X,ref:%i)", se_num, update->sdo_class, update->sdo_ref); rv = iasecc_sm_initialize(card, se_num, SM_CMD_SDO_UPDATE); LOG_TEST_RET(ctx, rv, "iasecc_sm_sdo_update() SM INITIALIZE failed"); sc_log(ctx, "current DF '%s'", sc_print_path(&sm_info->current_path_df)); sm_info->cmd_data = update; sc_remote_data_init(&rdata); rv = iasecc_sm_cmd(card, &rdata); LOG_TEST_RET(ctx, rv, "iasecc_sm_sdo_update() SM 'SDO UPDATE' failed"); rv = sm_release (card, &rdata, NULL, 0); LOG_TEST_RET(ctx, rv, "iasecc_sm_sdo_update() SM release failed"); rdata.free(&rdata); LOG_FUNC_RETURN(ctx, rv); #else LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "built without support of Secure-Messaging"); return SC_ERROR_NOT_SUPPORTED; #endif } int iasecc_sm_pin_reset(struct sc_card *card, unsigned se_num, struct sc_pin_cmd_data *data) { struct sc_context *ctx = card->ctx; #ifdef ENABLE_SM struct sm_info *sm_info = &card->sm_ctx.info; struct sc_remote_data rdata; int rv; LOG_FUNC_CALLED(ctx); 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"); sm_info->cmd_data = data; sc_remote_data_init(&rdata); rv = iasecc_sm_cmd(card, &rdata); LOG_TEST_RET(ctx, rv, "iasecc_sm_pin_reset() SM 'PIN RESET' failed"); rv = sm_release (card, &rdata, NULL, 0); LOG_TEST_RET(ctx, rv, "iasecc_sm_pin_reset() SM release failed"); rdata.free(&rdata); LOG_FUNC_RETURN(ctx, rv); #else LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "built without support of Secure-Messaging"); return SC_ERROR_NOT_SUPPORTED; #endif } int iasecc_sm_create_file(struct sc_card *card, unsigned se_num, unsigned char *fcp, size_t fcp_len) { struct sc_context *ctx = card->ctx; #ifdef ENABLE_SM struct sm_info *sm_info = &card->sm_ctx.info; struct sc_remote_data rdata; struct iasecc_sm_cmd_create_file cmd_data; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "iasecc_sm_create_file() SE#%i, fcp(%"SC_FORMAT_LEN_SIZE_T"u) '%s'", se_num, fcp_len, sc_dump_hex(fcp, fcp_len)); rv = iasecc_sm_initialize(card, se_num, SM_CMD_FILE_CREATE); LOG_TEST_RET(ctx, rv, "iasecc_sm_create_file() SM INITIALIZE failed"); cmd_data.data = fcp; cmd_data.size = fcp_len; sm_info->cmd_data = &cmd_data; sc_remote_data_init(&rdata); rv= iasecc_sm_cmd(card, &rdata); LOG_TEST_RET(ctx, rv, "iasecc_sm_create_file() SM 'UPDATE BINARY' failed"); rv = sm_release (card, &rdata, NULL, 0); LOG_TEST_RET(ctx, rv, "iasecc_sm_create_file() SM release failed"); rdata.free(&rdata); LOG_FUNC_RETURN(ctx, rv); #else LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "built without support of Secure-Messaging"); return SC_ERROR_NOT_SUPPORTED; #endif } int iasecc_sm_read_binary(struct sc_card *card, unsigned se_num, size_t offs, unsigned char *buff, size_t count) { struct sc_context *ctx = card->ctx; #ifdef ENABLE_SM struct sm_info *sm_info = &card->sm_ctx.info; struct sc_remote_data rdata; struct iasecc_sm_cmd_update_binary cmd_data; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "SM read binary: acl:%X, offs:%"SC_FORMAT_LEN_SIZE_T"u, count:%"SC_FORMAT_LEN_SIZE_T"u", se_num, offs, count); rv = iasecc_sm_initialize(card, se_num, SM_CMD_FILE_READ); LOG_TEST_RET(ctx, rv, "iasecc_sm_read_binary() SM INITIALIZE failed"); cmd_data.offs = offs; cmd_data.count = count; sm_info->cmd_data = &cmd_data; sc_remote_data_init(&rdata); rv = iasecc_sm_cmd(card, &rdata); LOG_TEST_RET(ctx, rv, "iasecc_sm_read_binary() SM 'READ BINARY' failed"); sc_log(ctx, "IAS/ECC decode answer() rdata length %i", rdata.length); rv = sm_release (card, &rdata, buff, count); LOG_TEST_RET(ctx, rv, "iasecc_sm_read_binary() SM release failed"); rdata.free(&rdata); LOG_FUNC_RETURN(ctx, rv); #else LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "built without support of Secure-Messaging"); return SC_ERROR_NOT_SUPPORTED; #endif } int iasecc_sm_update_binary(struct sc_card *card, unsigned se_num, size_t offs, const unsigned char *buff, size_t count) { struct sc_context *ctx = card->ctx; #ifdef ENABLE_SM struct sm_info *sm_info = &card->sm_ctx.info; struct sc_remote_data rdata; struct iasecc_sm_cmd_update_binary cmd_data; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "SM update binary: acl:%X, offs:%"SC_FORMAT_LEN_SIZE_T"u, count:%"SC_FORMAT_LEN_SIZE_T"u", se_num, offs, count); rv = iasecc_sm_initialize(card, se_num, SM_CMD_FILE_UPDATE); LOG_TEST_RET(ctx, rv, "iasecc_sm_update_binary() SM INITIALIZE failed"); cmd_data.offs = offs; cmd_data.count = count; cmd_data.data = buff; sm_info->cmd_data = &cmd_data; sc_remote_data_init(&rdata); rv = iasecc_sm_cmd(card, &rdata); LOG_TEST_RET(ctx, rv, "iasecc_sm_update_binary() SM 'UPDATE BINARY' failed"); rv = sm_release (card, &rdata, NULL, 0); LOG_TEST_RET(ctx, rv, "iasecc_sm_update_binary() SM release failed"); rdata.free(&rdata); 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; #endif } int iasecc_sm_delete_file(struct sc_card *card, unsigned se_num, unsigned int file_id) { struct sc_context *ctx = card->ctx; #ifdef ENABLE_SM struct sm_info *sm_info = &card->sm_ctx.info; struct sc_remote_data rdata; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "SM delete file: SE#:%X, file-id:%X", se_num, file_id); rv = iasecc_sm_initialize(card, se_num, SM_CMD_FILE_DELETE); LOG_TEST_RET(ctx, rv, "iasecc_sm_delete_file() SM INITIALIZE failed"); sm_info->cmd_data = (void *)(uintptr_t)file_id; sc_remote_data_init(&rdata); rv = iasecc_sm_cmd(card, &rdata); LOG_TEST_RET(ctx, rv, "iasecc_sm_delete_file() SM 'FILE DELETE' failed"); rv = sm_release (card, &rdata, NULL, 0); LOG_TEST_RET(ctx, rv, "iasecc_sm_delete_file() SM release failed"); rdata.free(&rdata); LOG_FUNC_RETURN(ctx, rv); #else LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "built without support of Secure-Messaging"); return SC_ERROR_NOT_SUPPORTED; #endif } OpenSC-0.26.1/src/libopensc/iasecc.h000066400000000000000000000067561474147347300171300ustar00rootroot00000000000000/* * iasecc.h Support for IAS/ECC smart cards * * Copyright (C) 2010 Viktor Tarasov * OpenTrust * * 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 _OPENSC_IASECC_H #define _OPENSC_IASECC_H #include "libopensc/errors.h" #include "libopensc/types.h" #include "libopensc/iasecc-sdo.h" #define ISO7812_PAN_SN_TAG 0x5A #ifndef SHA256_DIGEST_LENGTH #define SHA_DIGEST_LENGTH 20 #define SHA256_DIGEST_LENGTH 32 #endif #ifndef CKM_RSA_PKCS #define CKM_RSA_PKCS 0x00000001 #define CKM_SHA1_RSA_PKCS 0x00000006 #define CKM_SHA256_RSA_PKCS 0x00000040 #define CKM_SHA_1 0x00000220 #define CKM_SHA256 0x00000250 #endif #define IASECC_TITLE "IASECC" #define IASECC_FCP_TAG 0x62 #define IASECC_FCP_TAG_SIZE 0x80 #define IASECC_FCP_TAG_TYPE 0x82 #define IASECC_FCP_TAG_FID 0x83 #define IASECC_FCP_TAG_NAME 0x84 #define IASECC_FCP_TAG_SFID 0x88 #define IASECC_FCP_TAG_ACLS 0xA1 #define IASECC_FCP_TAG_ACLS_CONTACT 0x8C #define IASECC_FCP_TAG_ACLS_CONTACTLESS 0x9C #define IASECC_FCP_TYPE_EF 0x01 #define IASECC_FCP_TYPE_DF 0x38 #define IASECC_OBJECT_REF_LOCAL 0x80 #define IASECC_OBJECT_REF_GLOBAL 0x00 #define IASECC_OBJECT_REF_MIN 0x01 #define IASECC_OBJECT_REF_MAX 0x1F #define IASECC_SE_REF_MIN 0x01 #define IASECC_SE_REF_MAX 0x0F /* IAS/ECC interindustry data tags */ #define IASECC_ATR_TAG_IO_BUFFER_SIZES 0xE0 #define IASECC_SFI_EF_DIR 0x1E #define IASECC_SFI_EF_ATR 0x1D #define IASECC_SFI_EF_SN 0x1C #define IASECC_SFI_EF_DH 0x1B #define IASECC_READ_BINARY_LENGTH_MAX 0xE7 #define IASECC_PSO_HASH_TAG_PARTIAL 0x90 #define IASECC_PSO_HASH_TAG_REMAINING 0x80 #define IASECC_CARD_ANSWER_TAG_DATA 0x87 #define IASECC_CARD_ANSWER_TAG_SW 0x99 #define IASECC_CARD_ANSWER_TAG_MAC 0x8E #define IASECC_SM_DO_TAG_TLE 0x97 #define IASECC_SM_DO_TAG_TSW 0x99 #define IASECC_SM_DO_TAG_TCC 0x8E #define IASECC_SM_DO_TAG_TCG_ODD_INS 0x85 #define IASECC_SM_DO_TAG_TCG_EVEN_INS 0x87 #define IASECC_SM_DO_TAG_TCG 0x87 #define IASECC_SM_DO_TAG_TBR 0x85 struct sc_security_env; typedef struct iasecc_qsign_data { int hash_algo; unsigned char hash[SHA256_DIGEST_LENGTH]; size_t hash_size; unsigned char pre_hash[SHA256_DIGEST_LENGTH]; size_t pre_hash_size; unsigned char counter[8]; unsigned long counter_long; unsigned char last_block[64]; size_t last_block_size; } iasecc_qsign_data_t; struct iasecc_version { unsigned char ic_manufacturer; unsigned char ic_type; unsigned char os_version; unsigned char iasecc_version; }; struct iasecc_io_buffer_sizes { size_t send; size_t send_sc; size_t recv; size_t recv_sc; }; struct iasecc_private_data { struct iasecc_version version; struct iasecc_io_buffer_sizes max_sizes; struct sc_security_env security_env; size_t key_size; unsigned op_method, op_ref; struct iasecc_se_info *se_info; }; #endif OpenSC-0.26.1/src/libopensc/internal-winscard.h000066400000000000000000000374351474147347300213230ustar00rootroot00000000000000/* * MUSCLE SmartCard Development ( http://pcsclite.alioth.debian.org/pcsclite.html ) * * Copyright (C) 1999-2003 * David Corcoran * Copyright (C) 2002-2009 * Ludovic Rousseau * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. 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. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. */ #ifndef __INTERNAL_WINSCARD_H #define __INTERNAL_WINSCARD_H /* Mostly copied from pcsc-lite, this is the minimum required */ #if defined(HAVE_INTTYPES_H) #include #elif defined(HAVE_STDINT_H) #include #elif defined(_MSC_VER) typedef unsigned __int32 uint32_t; typedef unsigned __int16 uint16_t; typedef unsigned __int8 uint8_t; #else #warning no uint32_t type available, please contact opensc-devel@opensc-project.org #endif #ifdef HAVE_WINSCARD_H #include #ifdef __APPLE__ #include #endif // allow unicode built where SCARD_READERSTATE is defined as SCARD_READERSTATEW and SCardGetStatusChange renamed to SCardGetStatusChangeW #ifdef _WIN32 #ifdef UNICODE #define SCARD_READERSTATE SCARD_READERSTATEA #undef SCardGetStatusChange #endif #endif #else /* mingw32 does not have winscard.h */ #define MAX_ATR_SIZE 33 /**< Maximum ATR size */ #define SCARD_PROTOCOL_T0 0x0001 /**< T=0 active protocol. */ #define SCARD_PROTOCOL_T1 0x0002 /**< T=1 active protocol. */ #define SCARD_PROTOCOL_RAW 0x0004 /**< Raw active protocol. */ #define SCARD_STATE_UNAWARE 0x0000 /**< App wants status */ #define SCARD_STATE_IGNORE 0x0001 /**< Ignore this reader */ #define SCARD_STATE_CHANGED 0x0002 /**< State has changed */ #define SCARD_STATE_UNKNOWN 0x0004 /**< Reader unknown */ #define SCARD_STATE_UNAVAILABLE 0x0008 /**< Status unavailable */ #define SCARD_STATE_EMPTY 0x0010 /**< Card removed */ #define SCARD_STATE_PRESENT 0x0020 /**< Card inserted */ #define SCARD_STATE_EXCLUSIVE 0x0080 /**< Exclusive Mode */ #define SCARD_STATE_INUSE 0x0100 /**< Shared Mode */ #define SCARD_STATE_MUTE 0x0200 /**< Unresponsive card */ #define SCARD_STATE_UNPOWERED 0x0400 /**< Unpowered card */ #define SCARD_SHARE_EXCLUSIVE 0x0001 /**< Exclusive mode only */ #define SCARD_SHARE_SHARED 0x0002 /**< Shared mode only */ #define SCARD_SHARE_DIRECT 0x0003 /**< Raw mode only */ #define SCARD_LEAVE_CARD 0x0000 /**< Do nothing on close */ #define SCARD_RESET_CARD 0x0001 /**< Reset on close */ #define SCARD_UNPOWER_CARD 0x0002 /**< Power down on close */ #define SCARD_SCOPE_USER 0x0000 /**< Scope in user space */ #ifndef SCARD_S_SUCCESS /* conflict in mingw-w64 */ #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. */ #define SCARD_E_PROTO_MISMATCH 0x8010000F /**< The requested protocols are incompatible with the protocol currently in use with the smart card. */ #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. */ #define SCARD_W_RESET_CARD 0x80100068 /**< The smart card has been reset, so any shared state information is invalid. */ #define SCARD_W_REMOVED_CARD 0x80100069 /**< The smart card has been removed, so further communication is not possible. */ #endif typedef const BYTE *LPCBYTE; typedef long SCARDCONTEXT; /**< \p hContext returned by SCardEstablishContext() */ typedef SCARDCONTEXT *PSCARDCONTEXT; typedef SCARDCONTEXT *LPSCARDCONTEXT; typedef long SCARDHANDLE; /**< \p hCard returned by SCardConnect() */ typedef SCARDHANDLE *PSCARDHANDLE; typedef SCARDHANDLE *LPSCARDHANDLE; typedef struct { const char *szReader; void *pvUserData; unsigned long dwCurrentState; unsigned long dwEventState; unsigned long cbAtr; unsigned char rgbAtr[MAX_ATR_SIZE]; } SCARD_READERSTATE, *LPSCARD_READERSTATE; typedef struct _SCARD_IO_REQUEST { unsigned long dwProtocol; /* Protocol identifier */ unsigned long cbPciLength; /* Protocol Control Inf Length */ } SCARD_IO_REQUEST, *PSCARD_IO_REQUEST, *LPSCARD_IO_REQUEST; 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) #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); typedef LONG (PCSC_API *SCardReleaseContext_t)(SCARDCONTEXT hContext); typedef LONG (PCSC_API *SCardConnect_t)(SCARDCONTEXT hContext, LPCSTR szReader, DWORD dwShareMode, DWORD dwPreferredProtocols, LPSCARDHANDLE phCard, LPDWORD pdwActiveProtocol); typedef LONG (PCSC_API *SCardReconnect_t)(SCARDHANDLE hCard, DWORD dwShareMode, DWORD dwPreferredProtocols, DWORD dwInitialization, LPDWORD pdwActiveProtocol); typedef LONG (PCSC_API *SCardDisconnect_t)(SCARDHANDLE hCard, DWORD dwDisposition); typedef LONG (PCSC_API *SCardBeginTransaction_t)(SCARDHANDLE hCard); typedef LONG (PCSC_API *SCardEndTransaction_t)(SCARDHANDLE hCard, DWORD dwDisposition); typedef LONG (PCSC_API *SCardStatus_t)(SCARDHANDLE hCard, LPSTR mszReaderNames, LPDWORD pcchReaderLen, LPDWORD pdwState, LPDWORD pdwProtocol, LPBYTE pbAtr, LPDWORD pcbAtrLen); typedef LONG (PCSC_API *SCardGetStatusChange_t)(SCARDCONTEXT hContext, DWORD dwTimeout, SCARD_READERSTATE *rgReaderStates, DWORD cReaders); typedef LONG (PCSC_API *SCardCancel_t)(SCARDCONTEXT hContext); typedef LONG (PCSC_API *SCardControlOLD_t)(SCARDHANDLE hCard, LPCVOID pbSendBuffer, DWORD cbSendLength, LPVOID pbRecvBuffer, LPDWORD lpBytesReturned); typedef LONG (PCSC_API *SCardControl_t)(SCARDHANDLE hCard, DWORD dwControlCode, LPCVOID pbSendBuffer, DWORD cbSendLength, LPVOID pbRecvBuffer, DWORD cbRecvLength, LPDWORD lpBytesReturned); typedef LONG (PCSC_API *SCardTransmit_t)(SCARDHANDLE hCard, LPCSCARD_IO_REQUEST pioSendPci, LPCBYTE pbSendBuffer, DWORD cbSendLength, LPSCARD_IO_REQUEST pioRecvPci, LPBYTE pbRecvBuffer, LPDWORD pcbRecvLength); typedef LONG (PCSC_API *SCardListReaders_t)(SCARDCONTEXT hContext, LPCSTR mszGroups, LPSTR mszReaders, LPDWORD pcchReaders); 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 #ifdef _WIN32 #include #define SCARD_CTL_CODE(code) CTL_CODE(FILE_DEVICE_SMARTCARD,(code),METHOD_BUFFERED,FILE_ANY_ACCESS) #else #define SCARD_CTL_CODE(code) (0x42000000 + (code)) #endif #endif /** * PC/SC v2.02.05 part 10 reader tags */ #define CM_IOCTL_GET_FEATURE_REQUEST SCARD_CTL_CODE(3400) #define FEATURE_VERIFY_PIN_START 0x01 #define FEATURE_VERIFY_PIN_FINISH 0x02 #define FEATURE_MODIFY_PIN_START 0x03 #define FEATURE_MODIFY_PIN_FINISH 0x04 #define FEATURE_GET_KEY_PRESSED 0x05 #define FEATURE_VERIFY_PIN_DIRECT 0x06 #define FEATURE_MODIFY_PIN_DIRECT 0x07 #define FEATURE_MCT_READERDIRECT 0x08 #define FEATURE_MCT_UNIVERSAL 0x09 #define FEATURE_IFD_PIN_PROPERTIES 0x0A #define FEATURE_ABORT 0x0B #define FEATURE_SET_SPE_MESSAGE 0x0C #define FEATURE_VERIFY_PIN_DIRECT_APP_ID 0x0D #define FEATURE_MODIFY_PIN_DIRECT_APP_ID 0x0E #define FEATURE_WRITE_DISPLAY 0x0F #define FEATURE_GET_KEY 0x10 #define FEATURE_IFD_DISPLAY_PROPERTIES 0x11 #define FEATURE_GET_TLV_PROPERTIES 0x12 #define FEATURE_CCID_ESC_COMMAND 0x13 #define FEATURE_EXECUTE_PACE 0x20 #define PACE_FUNCTION_GetReaderPACECapabilities 0x01 #define PACE_FUNCTION_EstablishPACEChannel 0x02 #define PACE_FUNCTION_DestroyPACEChannel 0x03 #define PACE_CAPABILITY_eSign 0x10 #define PACE_CAPABILITY_eID 0x20 #define PACE_CAPABILITY_generic 0x40 #define PACE_CAPABILITY_DestroyPACEChannel 0x80 /* properties returned by FEATURE_GET_TLV_PROPERTIES */ #define PCSCv2_PART10_PROPERTY_wLcdLayout 1 #define PCSCv2_PART10_PROPERTY_bEntryValidationCondition 2 #define PCSCv2_PART10_PROPERTY_bTimeOut2 3 #define PCSCv2_PART10_PROPERTY_wLcdMaxCharacters 4 #define PCSCv2_PART10_PROPERTY_wLcdMaxLines 5 #define PCSCv2_PART10_PROPERTY_bMinPINSize 6 #define PCSCv2_PART10_PROPERTY_bMaxPINSize 7 #define PCSCv2_PART10_PROPERTY_sFirmwareID 8 #define PCSCv2_PART10_PROPERTY_bPPDUSupport 9 #define PCSCv2_PART10_PROPERTY_dwMaxAPDUDataSize 10 #define PCSCv2_PART10_PROPERTY_wIdVendor 11 #define PCSCv2_PART10_PROPERTY_wIdProduct 12 /* structures used (but not defined) in PCSC Part 10: * "IFDs with Secure Pin Entry Capabilities" */ /* Set structure elements alignment on bytes * http://gcc.gnu.org/onlinedocs/gcc/Structure_002dPacking-Pragmas.html */ #if defined(__APPLE__) || defined(sun) #pragma pack(1) #else #pragma pack(push, 1) #endif /** the structure must be 6-bytes long */ typedef struct { uint8_t tag; uint8_t length; uint32_t value; /**< This value is always in BIG ENDIAN format as documented in PCSC v2 part 10 ch 2.2 page 2. You can use ntohl() for example */ } PCSC_TLV_STRUCTURE; /** the wLangId and wPINMaxExtraDigit are 16-bits long so are subject to byte * ordering */ #define HOST_TO_CCID_16(x) (x) #define HOST_TO_CCID_32(x) (x) /** structure used with \ref FEATURE_VERIFY_PIN_DIRECT */ typedef struct { uint8_t bTimerOut; /**< timeout is seconds (00 means use default timeout) */ uint8_t bTimerOut2; /**< timeout in seconds after first key stroke */ uint8_t bmFormatString; /**< formatting options */ uint8_t bmPINBlockString; /**< bits 7-4 bit size of PIN length in APDU, * bits 3-0 PIN block size in bytes after * justification and formatting */ uint8_t bmPINLengthFormat; /**< bits 7-5 RFU, * bit 4 set if system units are bytes, clear if * system units are bits, * bits 3-0 PIN length position in system units */ uint16_t wPINMaxExtraDigit; /**< 0xXXYY where XX is minimum PIN size in digits, and YY is maximum PIN size in digits */ uint8_t bEntryValidationCondition; /**< Conditions under which PIN entry should * be considered complete */ uint8_t bNumberMessage; /**< Number of messages to display for PIN verification */ uint16_t wLangId; /**< Language for messages */ uint8_t bMsgIndex; /**< Message index (should be 00) */ uint8_t bTeoPrologue[3]; /**< T=1 block prologue field to use (fill with 00) */ uint32_t ulDataLength; /**< length of Data to be sent to the ICC */ uint8_t abData #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) [] /* valid C99 code */ #else [0] /* non-standard, but usually working code */ #endif ; /**< Data to send to the ICC */ } PIN_VERIFY_STRUCTURE; /** structure used with \ref FEATURE_MODIFY_PIN_DIRECT */ typedef struct { uint8_t bTimerOut; /**< timeout is seconds (00 means use default timeout) */ uint8_t bTimerOut2; /**< timeout in seconds after first key stroke */ uint8_t bmFormatString; /**< formatting options */ uint8_t bmPINBlockString; /**< bits 7-4 bit size of PIN length in APDU, * bits 3-0 PIN block size in bytes after * justification and formatting */ uint8_t bmPINLengthFormat; /**< bits 7-5 RFU, * bit 4 set if system units are bytes, clear if * system units are bits, * bits 3-0 PIN length position in system units */ uint8_t bInsertionOffsetOld; /**< Insertion position offset in bytes for the current PIN */ uint8_t bInsertionOffsetNew; /**< Insertion position offset in bytes for the new PIN */ uint16_t wPINMaxExtraDigit; /**< 0xXXYY where XX is minimum PIN size in digits, and YY is maximum PIN size in digits */ uint8_t bConfirmPIN; /**< Flags governing need for confirmation of new PIN */ uint8_t bEntryValidationCondition; /**< Conditions under which PIN entry should * be considered complete */ uint8_t bNumberMessage; /**< Number of messages to display for PIN verification*/ uint16_t wLangId; /**< Language for messages */ uint8_t bMsgIndex1; /**< index of 1st prompting message */ uint8_t bMsgIndex2; /**< index of 2d prompting message */ uint8_t bMsgIndex3; /**< index of 3d prompting message */ uint8_t bTeoPrologue[3]; /**< T=1 block prologue field to use (fill with 00) */ uint32_t ulDataLength; /**< length of Data to be sent to the ICC */ uint8_t abData #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) [] /* valid C99 code */ #else [0] /* non-standard, but usually working code */ #endif ; /**< Data to send to the ICC */ } PIN_MODIFY_STRUCTURE; /* PIN_PROPERTIES as defined (in/up to?) PC/SC 2.02.05 */ /* This only makes sense with old Windows drivers. To be removed some time in the future. */ #define PIN_PROPERTIES_v5 typedef struct { uint16_t wLcdLayout; /**< display characteristics */ uint16_t wLcdMaxCharacters; uint16_t wLcdMaxLines; uint8_t bEntryValidationCondition; uint8_t bTimeOut2; } PIN_PROPERTIES_STRUCTURE_v5; /* PIN_PROPERTIES as defined in PC/SC 2.02.06 and later */ typedef struct { uint16_t wLcdLayout; /**< display characteristics */ uint8_t bEntryValidationCondition; uint8_t bTimeOut2; } PIN_PROPERTIES_STRUCTURE; /* restore default structure elements alignment */ #if defined(__APPLE__) || defined(sun) #pragma pack() #else #pragma pack(pop) #endif #endif OpenSC-0.26.1/src/libopensc/internal.h000066400000000000000000000260501474147347300175020ustar00rootroot00000000000000/* * internal.h: Internal definitions for libopensc * * Copyright (C) 2001, 2002 Juha Yrjölä * 2005 The OpenSC project * * 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_INTERNAL_H #define _SC_INTERNAL_H #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef __cplusplus extern "C" { #endif #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" #endif #define SC_FILE_MAGIC 0x14426950 #ifndef _WIN32 #define msleep(t) usleep((t) * 1000) #else #define msleep(t) Sleep(t) #define sleep(t) Sleep((t) * 1000) #endif #ifndef MAX #define MAX(x, y) (((x) > (y)) ? (x) : (y)) #endif #ifndef MIN #define MIN(x, y) (((x) < (y)) ? (x) : (y)) #endif struct sc_atr_table { /* The atr fields are required to * be in aa:bb:cc hex format. */ const char *atr; /* The atrmask is logically AND'd with an * card atr prior to comparison with the * atr reference value above. */ const char *atrmask; const char *name; int type; unsigned long flags; /* Reference to card_atr configuration block, * available to user configured card entries. */ scconf_block *card_atr; }; /* Internal use only */ int _sc_add_reader(struct sc_context *ctx, struct sc_reader *reader); int _sc_parse_atr(struct sc_reader *reader); /* Add an ATR to the card driver's struct sc_atr_table */ int _sc_add_atr(struct sc_context *ctx, struct sc_card_driver *driver, struct sc_atr_table *src); int _sc_free_atr(struct sc_context *ctx, struct sc_card_driver *driver); /** * Convert an unsigned long into 4 bytes in big endian order * @param buf the byte array for the result, should be 4 bytes long * @param x the value to be converted * @return the buffer passed, containing the converted value */ u8 *ulong2bebytes(u8 *buf, unsigned long x); /** * Convert an unsigned long into 2 bytes in big endian order * @param buf the byte array for the result, should be 2 bytes long * @param x the value to be converted * @return the buffer passed, containing the converted value */ u8 *ushort2bebytes(u8 *buf, unsigned short x); /** * Convert 4 bytes in big endian order into an unsigned long * @param buf the byte array of 4 bytes * @return the converted value */ unsigned long bebytes2ulong(const u8 *buf); /** * Convert 2 bytes in big endian order into an unsigned short * @param buf the byte array of 2 bytes * @return the converted value */ unsigned short bebytes2ushort(const u8 *buf); /** * Convert 2 bytes in little endian order into an unsigned short * @param buf the byte array of 2 bytes * @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 * all card drivers user configured ATRs. */ scconf_block *_sc_match_atr_block(sc_context_t *ctx, struct sc_card_driver *driver, struct sc_atr *atr); /* 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, 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, size_t key_length, unsigned long flags, unsigned long exponent); 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); /********************************************************************/ /* pkcs1 padding/encoding functions */ /********************************************************************/ 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_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. * @param ctx IN sc_context_t object * @param flags IN the algorithm to use * @param in IN input buffer * @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 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 mod_bits, void *pMechanism); /** * Get the necessary padding and sec. env. flags. * @param ctx IN sc_contex_t object * @param iflags IN the desired algorithms flags * @param caps IN the card / key capabilities * @param pflags OUT the padding flags to use * @param salg OUT the security env. algorithm flag to use * @return SC_SUCCESS on success and an error code otherwise */ int sc_get_encoding_flags(sc_context_t *ctx, unsigned long iflags, unsigned long caps, unsigned long *pflags, unsigned long *salg); /********************************************************************/ /* mutex functions */ /********************************************************************/ /** * Creates a new sc_mutex object. Note: unless sc_mutex_set_mutex_funcs() * this function does nothing and always returns SC_SUCCESS. * @param ctx sc_context_t object with the thread context * @param mutex pointer for the newly created mutex object * @return SC_SUCCESS on success and an error code otherwise */ 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 * function does nothing and always returns SUCCESS. * @param ctx sc_context_t object with the thread context * @param mutex mutex object to lock * @return SC_SUCCESS on success and an error code otherwise */ int sc_mutex_lock(const sc_context_t *ctx, void *mutex); /** * Unlocks a sc_mutex object. Note: Unless sc_mutex_set_mutex_funcs() * has been called before this function does nothing and always returns * SC_SUCCESS. * @param ctx sc_context_t object with the thread context * @param mutex mutex object to unlock * @return SC_SUCCESS on success and an error code otherwise */ int sc_mutex_unlock(const sc_context_t *ctx, void *mutex); /** * Destroys a sc_mutex object. Note: Unless sc_mutex_set_mutex_funcs() * has been called before this function does nothing and always returns * SC_SUCCESS. * @param ctx sc_context_t object with the thread context * @param mutex mutex object to be destroyed * @return SC_SUCCESS on success and an error code otherwise */ int sc_mutex_destroy(const sc_context_t *ctx, void *mutex); /** * Returns a unique id for every thread. * @param ctx sc_context_t object with the thread context * @return unsigned long with the unique id or 0 if not supported */ unsigned long sc_thread_id(const sc_context_t *ctx); /********************************************************************/ /* internal APDU handling functions */ /********************************************************************/ /** * Returns the encoded APDU in newly created buffer. * @param ctx sc_context_t object * @param apdu sc_apdu_t object with the APDU to encode * @param buf pointer to the newly allocated buffer * @param len length of the encoded APDU * @param proto protocol to be used * @return SC_SUCCESS on success and an error code otherwise */ int sc_apdu_get_octets(sc_context_t *ctx, const sc_apdu_t *apdu, u8 **buf, size_t *len, unsigned int proto); /** * Sets the status bytes and return data in the APDU * @param ctx sc_context_t object * @param apdu the apdu to which the data should be written * @param buf returned data * @param len length of the returned data * @return SC_SUCCESS on success and an error code otherwise */ int sc_apdu_set_resp(sc_context_t *ctx, sc_apdu_t *apdu, const u8 *buf, size_t len); /** * Logs APDU * @param ctx sc_context_t object * @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, 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); #ifdef __cplusplus } #endif #endif OpenSC-0.26.1/src/libopensc/iso7816.c000066400000000000000000001266601474147347300170110ustar00rootroot00000000000000/* * iso7816.c: Functions specified by the ISO 7816 standard * * Copyright (C) 2001, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include #include #include #include #include "internal.h" #include "asn1.h" #include "iso7816.h" #include "sm/sm-iso.h" void iso7816_fixup_transceive_length(const struct sc_card *card, struct sc_apdu *apdu) { if (card == NULL || apdu == NULL) { return; } if (apdu->lc > sc_get_max_send_size(card)) { /* The lower layers will automatically do chaining */ apdu->flags |= SC_APDU_FLAGS_CHAINING; } if (apdu->le > sc_get_max_recv_size(card)) { /* The lower layers will automatically do a GET RESPONSE, if possible. * All other workarounds must be carried out by the upper layers. */ apdu->le = sc_get_max_recv_size(card); } } static const struct sc_card_error iso7816_errors[] = { { 0x6200, SC_ERROR_CARD_CMD_FAILED, "Warning: no information given, non-volatile memory is unchanged" }, { 0x6281, SC_ERROR_CORRUPTED_DATA, "Part of returned data may be corrupted" }, { 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" }, { 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" }, { 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,"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 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"}, { 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" }, }; static int iso7816_check_sw(struct sc_card *card, unsigned int sw1, unsigned int sw2) { const int err_count = sizeof(iso7816_errors)/sizeof(iso7816_errors[0]); int i; /* Handle special cases here */ if (sw1 == 0x6C) { sc_log(card->ctx, "Wrong length; correct length is %d", sw2); return SC_ERROR_WRONG_LENGTH; } if (sw1 == 0x90 && sw2 == 0x00) return SC_SUCCESS; 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); return iso7816_errors[i].errorno; } } sc_log(card->ctx, "Unknown SWs; SW1=%02X, SW2=%02X", sw1, sw2); return SC_ERROR_CARD_CMD_FAILED; } static int 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) { sc_log(ctx, "invalid EF offset: 0x%X > 0x7FFF", idx); return SC_ERROR_OFFSET_TOO_LARGE; } sc_format_apdu(card, &apdu, SC_APDU_CASE_2, 0xB0, (idx >> 8) & 0x7F, idx & 0xFF); apdu.le = count; apdu.resplen = count; apdu.resp = buf; iso7816_fixup_transceive_length(card, &apdu); 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 == SC_ERROR_FILE_END_REACHED) LOG_FUNC_RETURN(ctx, (int)apdu.resplen); LOG_TEST_RET(ctx, r, "Check SW error"); 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_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, unsigned int idx, u8 *buf, size_t count, unsigned long flags) { struct sc_apdu apdu; int r; /* 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; iso7816_fixup_transceive_length(card, &apdu); 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, "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; } err: free(encoded_data); free(decoded_data); LOG_FUNC_RETURN(card->ctx, r); } static int iso7816_write_record(struct sc_card *card, unsigned int rec_nr, const u8 *buf, size_t count, unsigned long flags) { struct sc_apdu apdu; int r; sc_format_apdu(card, &apdu, SC_APDU_CASE_3, 0xD2, rec_nr, 0); 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; 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, (int)count); } static int iso7816_append_record(struct sc_card *card, const u8 *buf, size_t count, unsigned long flags) { struct sc_apdu apdu; int r; sc_format_apdu(card, &apdu, SC_APDU_CASE_3, 0xE2, 0, 0); apdu.lc = count; apdu.datalen = count; apdu.data = buf; apdu.p2 = (flags & SC_RECORD_EF_ID_MASK) << 3; 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, (int)count); } static int 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, 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; iso7816_fixup_transceive_length(card, &apdu); 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, "Card returned error"); r = (int)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) { struct sc_apdu apdu; int r; if (idx > 0x7fff) { sc_log(card->ctx, "invalid EF offset: 0x%X > 0x7FFF", idx); return SC_ERROR_OFFSET_TOO_LARGE; } sc_format_apdu(card, &apdu, SC_APDU_CASE_3, 0xD0, (idx >> 8) & 0x7F, idx & 0xFF); apdu.lc = count; apdu.datalen = count; apdu.data = buf; 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, (int)count); } static int iso7816_update_binary(struct sc_card *card, unsigned int idx, const u8 *buf, size_t count, unsigned long flags) { struct sc_apdu apdu; int r; if (idx > 0x7fff) { sc_log(card->ctx, "invalid EF offset: 0x%X > 0x7FFF", idx); return SC_ERROR_OFFSET_TOO_LARGE; } sc_format_apdu(card, &apdu, SC_APDU_CASE_3, 0xD6, (idx >> 8) & 0x7F, idx & 0xFF); apdu.lc = count; apdu.datalen = count; apdu.data = buf; 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, (int)count); } static int iso7816_process_fci(struct sc_card *card, struct sc_file *file, const unsigned char *buf, size_t buflen) { struct sc_context *ctx = card->ctx; const unsigned char *p, *end; unsigned int cla = 0, tag = 0; size_t length; file->status = SC_FILE_STATUS_UNKNOWN; for (p = buf, length = buflen, end = buf + buflen; 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 (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 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 0x83: if (length == 2) { file->id = (p[0] << 8) | p[1]; sc_log(ctx, " file identifier: 0x%02X%02X", p[0], p[1]); } break; 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; case 0x86: if (SC_SUCCESS != sc_file_set_sec_attr(file, p, length)) { sc_log(ctx, "Warning: Could not set file security properties"); } break; case 0x88: if (length == 1) { file->sid = *p; sc_log(ctx, " short file identifier: 0x%02X", *p); } break; 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; 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; } static int iso7816_select_file(struct sc_card *card, const struct sc_path *in_path, struct sc_file **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; int select_mf = 0; struct sc_file *file = NULL; const u8 *buffer; size_t buffer_len; unsigned int cla, tag; if (card == NULL || in_path == NULL) { return SC_ERROR_INVALID_ARGUMENTS; } ctx = card->ctx; memcpy(path, in_path->value, in_path->len); pathlen = in_path->len; pathtype = in_path->type; 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_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); if (pathtype == SC_PATH_TYPE_PATH || pathtype == SC_PATH_TYPE_DF_NAME) pathtype = SC_PATH_TYPE_FROM_CURRENT; } } sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0, 0); switch (pathtype) { case SC_PATH_TYPE_FILE_ID: apdu.p1 = 0; if (pathlen != 2) return SC_ERROR_INVALID_ARGUMENTS; break; case SC_PATH_TYPE_DF_NAME: apdu.p1 = 4; break; case SC_PATH_TYPE_PATH: apdu.p1 = 8; if (pathlen >= 2 && memcmp(path, "\x3F\x00", 2) == 0) { if (pathlen == 2) { /* only 3F00 supplied */ select_mf = 1; apdu.p1 = 0; break; } path += 2; pathlen -= 2; } break; case SC_PATH_TYPE_FROM_CURRENT: apdu.p1 = 9; break; case SC_PATH_TYPE_PARENT: apdu.p1 = 3; pathlen = 0; apdu.cse = SC_APDU_CASE_2_SHORT; break; default: LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); } apdu.lc = pathlen; apdu.data = path; apdu.datalen = pathlen; if (file_out != NULL) { apdu.p2 = 0; /* first record, return FCI */ apdu.resp = buf; apdu.resplen = sizeof(buf); apdu.le = sc_get_max_recv_size(card) < 256 ? sc_get_max_recv_size(card) : 256; } else { apdu.p2 = 0x0C; /* first record, return nothing */ apdu.cse = (apdu.lc == 0) ? SC_APDU_CASE_1 : SC_APDU_CASE_3_SHORT; } 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; 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); if (file_out && (apdu.resplen == 0)) { /* For some cards 'SELECT' MF or DF_NAME do not return FCI. */ if (select_mf || pathtype == SC_PATH_TYPE_DF_NAME) { file = sc_file_new(); if (file == NULL) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); file->path = *in_path; *file_out = file; LOG_FUNC_RETURN(ctx, SC_SUCCESS); } } if (apdu.resplen < 2) LOG_FUNC_RETURN(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); switch (apdu.resp[0]) { case ISO7816_TAG_FCI: case ISO7816_TAG_FCP: file = sc_file_new(); if (file == NULL) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); file->path = *in_path; if (card->ops->process_fci == NULL) { sc_file_free(file); LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); } buffer = apdu.resp; r = sc_asn1_read_tag(&buffer, apdu.resplen, &cla, &tag, &buffer_len); if (r == SC_SUCCESS) card->ops->process_fci(card, file, buffer, buffer_len); *file_out = file; break; case 0x00: /* proprietary coding */ LOG_FUNC_RETURN(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); default: LOG_FUNC_RETURN(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); } return SC_SUCCESS; } static int iso7816_get_challenge(struct sc_card *card, u8 *rnd, size_t len) { int r; struct sc_apdu apdu; sc_format_apdu(card, &apdu, SC_APDU_CASE_2, 0x84, 0x00, 0x00); apdu.le = len; apdu.resp = rnd; apdu.resplen = 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); LOG_TEST_RET(card->ctx, r, "GET CHALLENGE failed"); if (len < apdu.resplen) { return (int) len; } return (int) apdu.resplen; } static int iso7816_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++ = 0x6F; p++; buf[0] = (file->size >> 8) & 0xFF; buf[1] = file->size & 0xFF; sc_asn1_put_tag(0x81, buf, 2, p, *outlen - (p - out), &p); if (file->type_attr_len) { assert(sizeof(buf) >= file->type_attr_len); memcpy(buf, file->type_attr, file->type_attr_len); sc_asn1_put_tag(0x82, buf, file->type_attr_len, p, *outlen - (p - out), &p); } else { buf[0] = file->shareable ? 0x40 : 0; switch (file->type) { case SC_FILE_TYPE_INTERNAL_EF: buf[0] |= 0x08; /* fall through */ case SC_FILE_TYPE_WORKING_EF: buf[0] |= file->ef_structure & 7; break; case SC_FILE_TYPE_DF: buf[0] |= 0x38; break; default: return SC_ERROR_NOT_SUPPORTED; } sc_asn1_put_tag(0x82, buf, 1, 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); /* 0x84 = DF name */ if (file->prop_attr_len) { assert(sizeof(buf) >= file->prop_attr_len); 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); memcpy(buf, file->sec_attr, file->sec_attr_len); sc_asn1_put_tag(0x86, buf, file->sec_attr_len, p, *outlen - (p - out), &p); } out[1] = p - out - 2; *outlen = p - out; return 0; } static int iso7816_create_file(struct sc_card *card, sc_file_t *file) { int r; size_t len; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; struct sc_apdu apdu; len = SC_MAX_APDU_BUFFER_SIZE; if (card->ops->construct_fci == NULL) LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); r = card->ops->construct_fci(card, file, sbuf, &len); LOG_TEST_RET(card->ctx, r, "construct_fci() failed"); 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); 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"); return r; } static int iso7816_get_response(struct sc_card *card, size_t *count, u8 *buf) { struct sc_apdu apdu = {0}; int r; size_t rlen; /* request at most max_recv_size bytes */ if (*count > sc_get_max_recv_size(card)) rlen = sc_get_max_recv_size(card); else rlen = *count; sc_format_apdu(card, &apdu, SC_APDU_CASE_2, 0xC0, 0x00, 0x00); apdu.le = rlen; apdu.resplen = rlen; apdu.resp = buf; /* don't call GET RESPONSE recursively */ apdu.flags |= SC_APDU_FLAGS_NO_GET_RESP; 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.sw1 == 0x90 && apdu.sw2 == 0x00) r = 0; /* no more data to read */ else if (apdu.sw1 == 0x61) r = apdu.sw2 == 0 ? 256 : apdu.sw2; /* more data to read */ else if (apdu.sw1 == 0x62 && apdu.sw2 == 0x82) r = 0; /* Le not reached but file/record ended */ else r = sc_check_sw(card, apdu.sw1, apdu.sw2); return r; } static int iso7816_delete_file(struct sc_card *card, const sc_path_t *path) { int r; u8 sbuf[2]; struct sc_apdu apdu; 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"); LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } if (path->len == 2) { sbuf[0] = path->value[0]; sbuf[1] = path->value[1]; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE4, 0x00, 0x00); apdu.lc = 2; apdu.datalen = 2; apdu.data = sbuf; } else { /* No file ID given: means currently selected file */ sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0xE4, 0x00, 0x00); } 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"); return r; } static int iso7816_set_security_env(struct sc_card *card, const struct sc_security_env *env, int se_num) { struct sc_apdu apdu; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; u8 *p; int r, locked = 0; if (card == NULL || env == NULL) { return SC_ERROR_INVALID_ARGUMENTS; } 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 = 0xB6; break; default: return SC_ERROR_INVALID_ARGUMENTS; } p = sbuf; if (env->flags & SC_SEC_ENV_ALG_REF_PRESENT) { *p++ = 0x80; /* algorithm reference */ *p++ = 0x01; *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++ = (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 (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; 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 = (int)(p - sbuf); apdu.lc = r; apdu.datalen = r; 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 != 0) { 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_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0xF2, se_num); r = sc_transmit_apdu(card, &apdu); sc_unlock(card); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); err: if (locked) sc_unlock(card); return r; } static int iso7816_restore_security_env(struct sc_card *card, int se_num) { struct sc_apdu apdu; int r; if (card == NULL) { return SC_ERROR_INVALID_ARGUMENTS; } sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x22, 0xF3, se_num); 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"); return r; } static int iso7816_compute_signature(struct sc_card *card, const u8 * data, size_t datalen, u8 * out, size_t outlen) { int r; struct sc_apdu apdu; if (card == NULL || data == NULL || out == NULL) { return SC_ERROR_INVALID_ARGUMENTS; } LOG_FUNC_CALLED(card->ctx); sc_log(card->ctx, "ISO7816 compute signature: in-len %"SC_FORMAT_LEN_SIZE_T"u, out-len %"SC_FORMAT_LEN_SIZE_T"u", datalen, 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, 0x2A, 0x9E, 0x9A); apdu.resp = out; apdu.resplen = outlen; apdu.le = outlen; apdu.data = data; apdu.lc = datalen; apdu.datalen = datalen; 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); r = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(card->ctx, r, "Card returned error"); LOG_FUNC_RETURN(card->ctx, r); } static int iso7816_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; if (card == NULL || crgram == NULL || out == NULL) { return SC_ERROR_INVALID_ARGUMENTS; } LOG_FUNC_CALLED(card->ctx); sc_log(card->ctx, "ISO7816 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] = 0; /* padding indicator byte, 0x00 = No further indication */ memcpy(sbuf + 1, crgram, crgram_len); apdu.data = sbuf; apdu.lc = crgram_len + 1; apdu.datalen = crgram_len + 1; 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, (int)apdu.resplen); else LOG_FUNC_RETURN(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2)); } 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) { int r, len = 0, pad = 0, use_pin_pad = 0, ins, p1 = 0; int cse = SC_APDU_CASE_3_SHORT; switch (data->pin_type) { case SC_AC_CHV: /* fall through */ case SC_AC_SESSION: case SC_AC_CONTEXT_SPECIFIC: break; default: return SC_ERROR_INVALID_ARGUMENTS; } 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 = 5; 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; break; case SC_PIN_CMD_CHANGE: ins = 0x24; if (data->pin1.len != 0 || (use_pin_pad && !( data->flags & SC_PIN_CMD_IMPLICIT_CHANGE))) { if ((r = sc_build_pin(buf, buf_len, &data->pin1, pad)) < 0) return r; len += r; } else { /* implicit test */ p1 = 1; } data->pin2.offset = data->pin1.offset + len; if ((r = sc_build_pin(buf+len, buf_len-len, &data->pin2, pad)) < 0) return r; /* Special case - where provided the old PIN on the command line * but expect the new one to be entered on the keypad. */ if (data->pin1.len && data->pin2.len == 0) { sc_log(card->ctx, "Special case - initial pin provided - but new pin asked on keypad"); data->flags |= SC_PIN_CMD_IMPLICIT_CHANGE; }; len += r; break; case SC_PIN_CMD_UNBLOCK: ins = 0x2C; if (data->pin1.len != 0 || (use_pin_pad && !( data->flags & SC_PIN_CMD_IMPLICIT_CHANGE))) { if ((r = sc_build_pin(buf, buf_len, &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; if ((r = sc_build_pin(buf+len, buf_len-len, &data->pin2, pad)) < 0) return r; len += r; } 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; /* No data to send or to receive */ cse = SC_APDU_CASE_1; break; default: return SC_ERROR_NOT_SUPPORTED; } sc_format_apdu(card, apdu, cse, ins, p1, data->pin_reference); apdu->lc = len; apdu->datalen = len; apdu->data = buf; apdu->resplen = 0; return 0; } static int iso7816_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_left) { 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 * 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"); return SC_ERROR_NOT_SUPPORTED; } /* See if we've been called from another card driver, which is * passing an APDU to us (this allows to write card drivers * whose PIN functions behave "mostly like ISO" except in some * special circumstances. */ if (data->apdu == NULL) { r = iso7816_build_pin_apdu(card, &local_apdu, data, sbuf, sizeof(sbuf)); if (r < 0) return r; data->apdu = &local_apdu; } apdu = data->apdu; if (!(data->flags & SC_PIN_CMD_USE_PINPAD) || data->cmd == SC_PIN_CMD_GET_INFO) { /* Transmit the APDU to the card */ r = sc_transmit_apdu(card, apdu); /* Clear the buffer - it may contain pins */ sc_mem_clear(sbuf, sizeof(sbuf)); } else { /* Call the reader driver to collect * the PIN and pass on the APDU to the card */ if (data->pin1.offset == 0) { sc_log(card->ctx, "Card driver didn't set PIN offset"); return SC_ERROR_INVALID_ARGUMENTS; } if (card->reader && card->reader->ops && card->reader->ops->perform_verify) { r = card->reader->ops->perform_verify(card->reader, data); /* sw1/sw2 filled in by reader driver */ } else { sc_log(card->ctx, "Card reader driver does not support " "PIN entry through reader key pad"); r = SC_ERROR_NOT_SUPPORTED; } } /* Don't pass references to local variables up to the caller. */ if (data->apdu == &local_apdu) data->apdu = NULL; LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu->sw1, apdu->sw2); if (r == SC_SUCCESS) { data->pin1.logged_in = SC_PIN_STATE_LOGGED_IN; } else if (r == SC_ERROR_PIN_CODE_INCORRECT) { data->pin1.tries_left = apdu->sw2 & 0xF; data->pin1.logged_in = SC_PIN_STATE_LOGGED_OUT; if (data->cmd == SC_PIN_CMD_GET_INFO) r = SC_SUCCESS; } else if (r == SC_ERROR_AUTH_METHOD_BLOCKED) { data->pin1.tries_left = 0; data->pin1.logged_in = SC_PIN_STATE_LOGGED_OUT; if (data->cmd == SC_PIN_CMD_GET_INFO) r = SC_SUCCESS; } if (tries_left != NULL) { *tries_left = data->pin1.tries_left; } return r; } static int iso7816_get_data(struct sc_card *card, unsigned int tag, u8 *buf, size_t len) { int r, cse; struct sc_apdu apdu; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (buf && len) cse = SC_APDU_CASE_2; else cse = SC_APDU_CASE_1; sc_format_apdu(card, &apdu, cse, 0xCA, (tag >> 8) & 0xff, tag & 0xff); apdu.le = len; apdu.resp = buf; apdu.resplen = 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); LOG_TEST_RET(card->ctx, r, "GET_DATA returned error"); if (apdu.resplen > len) r = SC_ERROR_WRONG_LENGTH; else r = (int)apdu.resplen; LOG_FUNC_RETURN(card->ctx, r); } static int iso7816_init(struct sc_card *card) { #if ENABLE_SM memset(&card->sm_ctx, 0, sizeof card->sm_ctx); #endif return SC_SUCCESS; } static int no_match(struct sc_card *card) { return 0; } static struct sc_card_operations iso_ops = { no_match, iso7816_init, /* init */ NULL, /* finish */ iso7816_read_binary, iso7816_write_binary, iso7816_update_binary, NULL, /* erase_binary */ iso7816_read_record, iso7816_write_record, iso7816_append_record, iso7816_update_record, iso7816_select_file, iso7816_get_response, iso7816_get_challenge, NULL, /* verify */ NULL, /* logout */ iso7816_restore_security_env, iso7816_set_security_env, iso7816_decipher, iso7816_compute_signature, NULL, /* change_reference_data */ NULL, /* reset_retry_counter */ iso7816_create_file, iso7816_delete_file, NULL, /* list_files */ iso7816_check_sw, NULL, /* card_ctl */ iso7816_process_fci, iso7816_construct_fci, iso7816_pin_cmd, iso7816_get_data, NULL, /* put_data */ NULL, /* delete_record */ NULL, /* read_public_key */ NULL, /* card_reader_lock_obtained */ NULL, /* wrap */ NULL, /* unwrap */ NULL, /* encrypt_sym */ NULL /* decrypt_sym */ }; static struct sc_card_driver iso_driver = { "ISO 7816 reference driver", "iso7816", &iso_ops, NULL, 0, NULL }; struct sc_card_driver * sc_get_iso7816_driver(void) { return &iso_driver; } #define ISO_READ_BINARY 0xB0 #define ISO_P1_FLAG_SFID 0x80 int iso7816_read_binary_sfid(sc_card_t *card, unsigned char sfid, u8 **ef, size_t *ef_len) { int r; size_t read; sc_apdu_t apdu; u8 *p; if (!card || !ef || !ef_len) { r = SC_ERROR_INVALID_ARGUMENTS; goto err; } *ef_len = 0; read = card->max_recv_size; sc_format_apdu(card, &apdu, SC_APDU_CASE_2, ISO_READ_BINARY, ISO_P1_FLAG_SFID|sfid, 0); p = realloc(*ef, read); if (!p) { r = SC_ERROR_OUT_OF_MEMORY; goto err; } *ef = p; apdu.resp = *ef; apdu.resplen = read; apdu.le = read; r = sc_transmit_apdu(card, &apdu); 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) { if (*ef_len > 0) break; else { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not read EF."); goto err; } } *ef_len += r; p = realloc(*ef, *ef_len + read); if (!p) { r = SC_ERROR_OUT_OF_MEMORY; goto err; } *ef = p; r = iso7816_read_binary(card, (unsigned)*ef_len, *ef + *ef_len, read, 0); } r = (int)*ef_len; err: return r; } #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; sc_apdu_t apdu; #ifdef ENABLE_SM struct iso_sm_ctx *iso_sm_ctx; #endif if (!card) { r = SC_ERROR_INVALID_ARGUMENTS; goto err; } #ifdef ENABLE_SM iso_sm_ctx = card->sm_ctx.info.cmd_data; if (write > SC_MAX_APDU_BUFFER_SIZE-2 || (card->sm_ctx.sm_mode == SM_MODE_TRANSMIT && write > (((SC_MAX_APDU_BUFFER_SIZE-2 /* 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. */ -18) / iso_sm_ctx->block_length) * iso_sm_ctx->block_length - 1))) sc_format_apdu(card, &apdu, SC_APDU_CASE_3_EXT, ISO_UPDATE_BINARY, ISO_P1_FLAG_SFID|sfid, 0); else #endif sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, ISO_UPDATE_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 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_update_binary(card, (unsigned)wrote, ef, write, 0); } 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; } OpenSC-0.26.1/src/libopensc/iso7816.h000066400000000000000000000025751474147347300170140ustar00rootroot00000000000000/* * iso7816.h: ISO-7816 defines */ #ifndef _ISO7816_TYPES_H #define _ISO7816_TYPES_H #ifdef __cplusplus extern "C" { #endif #define ISO7816_FILE_TYPE_TRANSPARENT_EF 0x01 #define ISO7816_FILE_TYPE_DF 0x38 #define ISO7816_TAG_FCI 0x6F #define ISO7816_TAG_FCP 0x62 #define ISO7816_TAG_FCP_SIZE 0x80 #define ISO7816_TAG_FCP_SIZE_FULL 0x81 #define ISO7816_TAG_FCP_TYPE 0x82 #define ISO7816_TAG_FCP_FID 0x83 #define ISO7816_TAG_FCP_DF_NAME 0x84 #define ISO7816_TAG_FCP_PROP_INFO 0x85 #define ISO7816_TAG_FCP_ACLS 0x86 #define ISO7816_TAG_FCP_LCS 0x8A /* ISO7816 interindustry data tags */ #define ISO7816_II_CATEGORY_TLV 0x80 #define ISO7816_II_CATEGORY_NOT_TLV 0x00 #define ISO7816_TAG_II_CARD_SERVICE 0x43 #define ISO7816_TAG_II_INITIAL_ACCESS_DATA 0x44 #define ISO7816_TAG_II_CARD_ISSUER_DATA 0x45 #define ISO7816_TAG_II_PRE_ISSUING 0x46 #define ISO7816_TAG_II_CARD_CAPABILITIES 0x47 #define ISO7816_TAG_II_AID 0x4F #define ISO7816_TAG_II_ALLOCATION_SCHEME 0x78 #define ISO7816_TAG_II_STATUS_LCS 0x81 #define ISO7816_TAG_II_STATUS_SW 0x82 #define ISO7816_TAG_II_STATUS_LCS_SW 0x83 #define ISO7816_TAG_II_EXTENDED_LENGTH 0x7F66 #define ISO7816_CAP_CHAINING 0x80 #define ISO7816_CAP_EXTENDED_LENGTH 0x40 #define ISO7816_CAP_EXTENDED_LENGTH_INFO 0x20 /* Other interindustry data tags */ #define IASECC_TAG_II_IO_BUFFER_SIZES 0xE0 #ifdef __cplusplus } #endif #endif OpenSC-0.26.1/src/libopensc/itacns.h000066400000000000000000000005031474147347300171420ustar00rootroot00000000000000#ifndef _OPENSC_ITACNS_H #define _OPENSC_ITACNS_H typedef struct { u8 ic_manufacturer_code; u8 mask_manufacturer_code; } itacns_drv_data_t; #define ITACNS_ICMAN_INFINEON 0x05 #define ITACNS_MASKMAN_IDEMIA 0x05 #define ITACNS_MASKMAN_SIEMENS 0x08 #define ITACNS_MASKMAN_STINCARD 0x09 #endif /* _OPENSC_ITACNS_H */ OpenSC-0.26.1/src/libopensc/jpki.h000066400000000000000000000025721474147347300166260ustar00rootroot00000000000000/* * jpki.h: Support for JPKI(Japanese Individual Number Cards). * * Copyright (C) 2016, HAMANO Tsukasa * * 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 _OPENSC_JPKI_H #define _OPENSC_JPKI_H #define SELECT_MF 0 #define SELECT_JPKI_AP 1 #define AID_JPKI "D392f000260100000001" #define JPKI_AUTH_KEY "0017" #define JPKI_AUTH_PIN "0018" #define JPKI_AUTH_PIN_MAX_TRIES 3 #define JPKI_SIGN_KEY "001A" #define JPKI_SIGN_PIN "001B" #define JPKI_SIGN_PIN_MAX_TRIES 5 #define JPKI_DRVDATA(card) ((struct jpki_private_data *) ((card)->drv_data)) struct jpki_private_data { sc_file_t *mf; int selected; int logged_in; }; int jpki_select_ap(struct sc_card *card); #endif OpenSC-0.26.1/src/libopensc/libopensc.exports000066400000000000000000000202611474147347300211170ustar00rootroot00000000000000scconf_block_add scconf_block_copy scconf_block_destroy scconf_find_block scconf_find_blocks scconf_find_list scconf_free scconf_get_bool scconf_get_int scconf_get_str scconf_item_add scconf_item_copy scconf_item_destroy scconf_list_add scconf_list_array_length scconf_list_copy scconf_list_destroy scconf_list_strdup scconf_list_strings_length scconf_list_toarray scconf_new scconf_parse scconf_parse_string scconf_put_bool scconf_put_int scconf_put_str scconf_write _sc_asn1_decode _sc_asn1_encode sc_append_file_id sc_append_path sc_append_path_id sc_append_record sc_asn1_clear_algorithm_id sc_asn1_decode sc_asn1_decode_algorithm_id sc_asn1_decode_bit_string sc_asn1_decode_bit_string_ni sc_asn1_decode_choice sc_asn1_decode_integer sc_asn1_decode_object_id sc_asn1_encode sc_asn1_encode_object_id sc_asn1_encode_algorithm_id sc_asn1_read_tag sc_asn1_find_tag sc_asn1_print_tags sc_asn1_put_tag sc_asn1_skip_tag sc_asn1_verify_tag sc_asn1_write_element sc_asn1_sig_value_sequence_to_rs sc_asn1_sig_value_rs_to_sequence sc_aux_data_set_md_flags sc_aux_data_allocate sc_aux_data_set_md_guid sc_aux_data_free sc_aux_data_get_md_guid sc_base64_decode sc_base64_encode sc_bin_to_hex sc_build_pin sc_cancel sc_card_ctl sc_change_reference_data sc_check_sw sc_compare_oid sc_compare_path sc_compare_path_prefix sc_compute_signature sc_concatenate_path sc_connect_card sc_context_create sc_copy_asn1_entry sc_create_file sc_ctx_detect_readers sc_ctx_get_reader sc_ctx_get_reader_by_id sc_ctx_get_reader_by_name sc_ctx_get_reader_count sc_ctx_log_to_file 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 sc_file_add_acl_entry sc_file_clear_acl_entries sc_file_dup sc_file_free sc_file_get_acl_entry sc_file_new sc_file_set_prop_attr sc_file_set_sec_attr 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_valid_oid sc_format_path sc_free_apps sc_free_ef_atr sc_get_cache_dir sc_get_challenge sc_get_conf_block sc_get_data sc_get_mf_path sc_get_version sc_hex_dump sc_dump_hex sc_hex_to_bin sc_list_files sc_lock sc_logout sc_make_cache_dir sc_mem_clear sc_mem_secure_alloc sc_mem_secure_free sc_mem_reverse sc_match_atr_block sc_path_print sc_path_set sc_pin_cmd sc_pkcs1_encode sc_pkcs15_add_df sc_pkcs15_add_object sc_pkcs15_add_unusedspace sc_pkcs15_bind sc_pkcs15_bind_synthetic sc_pkcs15_cache_file sc_pkcs15_card_clear sc_pkcs15_card_free sc_pkcs15_card_new sc_pkcs15_change_pin sc_pkcs15_compare_id sc_pkcs15_compute_signature sc_pkcs15_decipher sc_pkcs15_decode_aodf_entry sc_pkcs15_decode_cdf_entry sc_pkcs15_decode_dodf_entry sc_pkcs15_decode_prkdf_entry sc_pkcs15_decode_pubkey sc_pkcs15_decode_pubkey_rsa sc_pkcs15_decode_pubkey_ec sc_pkcs15_decode_pubkey_gostr3410 sc_pkcs15_decode_pukdf_entry sc_pkcs15_decode_skdf_entry sc_pkcs15_derive sc_pkcs15_encode_aodf_entry sc_pkcs15_encode_cdf_entry sc_pkcs15_encode_df sc_pkcs15_encode_dodf_entry sc_pkcs15_encode_odf sc_pkcs15_encode_prkdf_entry sc_pkcs15_encode_pubkey 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_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 sc_pkcs15_find_data_object_by_id sc_pkcs15_find_data_object_by_name sc_pkcs15_find_object_by_id sc_pkcs15_find_pin_by_auth_id sc_pkcs15_find_pin_by_flags sc_pkcs15_find_pin_by_reference sc_pkcs15_find_prkey_by_id sc_pkcs15_find_prkey_by_id_usage sc_pkcs15_find_prkey_by_reference sc_pkcs15_find_pubkey_by_id sc_pkcs15_find_skey_by_id sc_pkcs15_find_so_pin sc_pkcs15_fix_ec_parameters sc_pkcs15_format_id sc_pkcs15_free_cert_info sc_pkcs15_free_certificate sc_pkcs15_free_data_info sc_pkcs15_free_data_object sc_pkcs15_free_key_params sc_pkcs15_free_object sc_pkcs15_free_auth_info 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 sc_pkcs15_get_object_id sc_pkcs15_get_objects sc_pkcs15_get_objects_cond sc_pkcs15_get_lastupdate sc_pkcs15_serialize_guid sc_pkcs15_hex_string_to_id sc_pkcs15_is_emulation_only sc_pkcs15_make_absolute_path sc_pkcs15_parse_df sc_pkcs15_parse_tokeninfo sc_pkcs15_parse_unusedspace sc_pkcs15_pincache_clear sc_pkcs15_print_id sc_pkcs15_prkey_attrs_from_cert sc_pkcs15_read_cached_file sc_pkcs15_read_certificate sc_pkcs15_read_data_object sc_pkcs15_read_file sc_pkcs15_read_pubkey sc_pkcs15_pubkey_from_prvkey 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 sc_pkcs15emu_add_data_object sc_pkcs15emu_add_pin_obj sc_pkcs15emu_add_rsa_prkey sc_pkcs15emu_add_rsa_pubkey sc_pkcs15emu_add_ec_prkey sc_pkcs15emu_add_ec_pubkey sc_pkcs15emu_add_x509_cert sc_pkcs15emu_object_add sc_pkcs15_bind_internal sc_print_path sc_put_data sc_read_binary sc_read_record sc_release_context sc_reset sc_reset_retry_counter sc_restore_security_env sc_select_file sc_set_card_driver 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 sc_get_iso7816_driver iso7816_write_binary_sfid iso7816_read_binary_sfid sc_pkcs15init_add_app sc_pkcs15init_authenticate sc_pkcs15init_bind sc_pkcs15init_change_attrib sc_pkcs15init_create_file sc_pkcs15init_delete_by_path sc_pkcs15init_delete_object sc_pkcs15init_erase_card sc_pkcs15init_erase_card_recursively sc_pkcs15init_finalize_card sc_pkcs15init_fixup_file sc_pkcs15init_generate_key sc_pkcs15init_generate_secret_key sc_pkcs15init_get_asepcos_ops sc_pkcs15init_get_cardos_ops sc_pkcs15init_get_cryptoflex_ops sc_pkcs15init_get_cyberflex_ops sc_pkcs15init_get_gids_ops sc_pkcs15init_get_manufacturer sc_pkcs15init_get_muscle_ops sc_pkcs15init_get_oberthur_ops sc_pkcs15init_get_pin_info sc_pkcs15init_get_rutoken_ops sc_pkcs15init_get_rtecp_ops sc_pkcs15init_get_serial sc_pkcs15init_get_setcos_ops sc_pkcs15init_get_starcos_ops sc_pkcs15init_rmdir sc_pkcs15init_set_callbacks sc_pkcs15init_set_lifecycle sc_pkcs15init_set_p15card sc_pkcs15init_set_serial sc_pkcs15init_store_certificate sc_pkcs15init_store_data_object sc_pkcs15init_store_pin sc_pkcs15init_store_private_key sc_pkcs15init_store_public_key sc_pkcs15init_store_secret_key sc_pkcs15init_unbind sc_pkcs15init_update_any_df sc_pkcs15init_update_certificate sc_pkcs15init_update_file 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 sc_pkcs15_convert_prkey sc_pkcs15_convert_pubkey sc_sm_parse_answer sc_sm_update_apdu_response sc_sm_single_transmit sc_sm_stop iasecc_sm_create_file iasecc_sm_delete_file iasecc_sm_external_authentication iasecc_sm_pin_reset iasecc_sm_pin_verify iasecc_sm_read_binary iasecc_sm_rsa_generate iasecc_sm_rsa_update iasecc_sm_update_binary iasecc_sm_sdo_update iasecc_sdo_encode_update_field _sc_card_add_ec_alg _sc_card_add_rsa_alg _sc_match_atr _sc_match_atr_block _sc_log _sc_log_openssl eac_secret_name get_pace_capabilities perform_pace perform_terminal_authentication perform_chip_authentication eac_default_flags eac_pace_get_tries_left npa_reset_retry_counter 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 OpenSC-0.26.1/src/libopensc/log.c000066400000000000000000000250721474147347300164450ustar00rootroot00000000000000/* * log.c: Miscellaneous logging functions * * Copyright (C) 2001, 2002 Juha Yrjölä * Copyright (C) 2003 Antti Tapaninen * * 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 #include #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_IO_H #include #endif #ifdef HAVE_PTHREAD #include #endif #ifdef _WIN32 #include #endif #ifdef ENABLE_OPENSSL #include #endif /* ENABLE_OPENSSL */ #include "internal.h" 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, 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, 0, format, 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) { #ifdef _WIN32 SYSTEMTIME st; #else struct tm *tm; struct timeval tv; char time_string[40]; #endif if (!ctx || ctx->debug < level) return; #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); 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); sc_color_fprintf(SC_COLOR_FG_GREEN, ctx, ctx->debug_file, " %s.%03ld", time_string, (long)tv.tv_usec / 1000); #endif 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 (file != NULL) { sc_color_fprintf(SC_COLOR_FG_YELLOW, ctx, ctx->debug_file, "%s:%d:%s: ", file, line, func ? func : ""); } 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); #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, ...) { va_list ap; va_start(ap, format); sc_do_log_va(ctx, level, NULL, 0, NULL, 0, format, ap); va_end(ap); } 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, 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, const char *func, const char *label, const u8 *data, size_t len) { size_t blen = len * 5 + 128; char *buf = malloc(blen); if (buf == NULL) return; sc_hex_dump(data, len, buf, blen); if (label) sc_do_log(ctx, type, file, line, func, "\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, "%"SC_FORMAT_LEN_SIZE_T"u byte%s:\n%s", len, len==1?"":"s", buf); free(buf); } void sc_hex_dump(const u8 * in, size_t count, char *buf, size_t len) { char *p = buf; int lines = 0; if (buf == NULL || (in == NULL && count != 0)) { return; } buf[0] = 0; if ((count * 5) > len) return; while (count) { char ascbuf[17]; size_t i; for (i = 0; i < count && i < 16; i++) { sprintf(p, "%02X ", *in); if (isprint(*in)) ascbuf[i] = *in; else ascbuf[i] = '.'; p += 3; in++; } count -= i; ascbuf[i] = 0; for (; i < 16 && lines; i++) { strcat(p, " "); p += 3; } strcat(p, ascbuf); p += strlen(p); sprintf(p, "\n"); p++; lines++; } } const char * sc_dump_hex(const u8 * in, size_t count) { static char dump_buf[0x1000]; size_t ii, size = sizeof(dump_buf) - 0x10; size_t offs = 0; memset(dump_buf, 0, sizeof(dump_buf)); if (in == NULL) return dump_buf; for (ii=0; ii size) break; } if (iivalue[ii] != -1; ii++) snprintf(dump_buf + strlen(dump_buf), sizeof(dump_buf) - strlen(dump_buf), "%s%i", (ii ? "." : ""), oid->value[ii]); return dump_buf; } OpenSC-0.26.1/src/libopensc/log.h000066400000000000000000000160021474147347300164430ustar00rootroot00000000000000/* * log.h: Logging functions header file * * Copyright (C) 2001, 2002 Juha Yrjölä * Copyright (C) 2003 Antti Tapaninen * * 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 _OPENSC_LOG_H #define _OPENSC_LOG_H #ifdef __cplusplus extern "C" { #endif #include #include "libopensc/opensc.h" enum { SC_LOG_DEBUG_VERBOSE_TOOL = 1, /* tools only: verbose */ SC_LOG_DEBUG_VERBOSE, /* helps users */ SC_LOG_DEBUG_NORMAL, /* helps developers */ SC_LOG_DEBUG_RFU1, /* RFU */ 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, 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__) #if defined(__MINGW32__) && defined (__MINGW_PRINTF_FORMAT) #define SC_PRINTF_FORMAT __MINGW_PRINTF_FORMAT #else #define SC_PRINTF_FORMAT printf #endif /* GCC can check format and param correctness for us */ 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 * * @param[in] ctx Context for logging * @param[in] level * @param[in] label Label to prepend to the buffer * @param[in] data Binary data * @param[in] len Length of \a data */ #define sc_debug_hex(ctx, level, 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] 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 * @param[in] label label to prepend to the buffer * @param[in] data binary data * @param[in] len length of \a data */ 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(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, 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_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, FILENAME, __LINE__, __FUNCTION__, \ "returning with: %d\n", _ret); \ } \ return _ret; \ } while(0) #define LOG_FUNC_RETURN(ctx, r) SC_FUNC_RETURN((ctx), SC_LOG_DEBUG_NORMAL, (r)) #define SC_TEST_RET(ctx, level, r, text) do { \ int _ret = (r); \ if (_ret < 0) { \ sc_do_log_color(ctx, level, FILENAME, __LINE__, __FUNCTION__, SC_COLOR_FG_RED, \ "%s: %d (%s)\n", (text), _ret, sc_strerror(_ret)); \ return _ret; \ } \ } while(0) #define LOG_TEST_RET(ctx, r, text) SC_TEST_RET((ctx), SC_LOG_DEBUG_NORMAL, (r), (text)) #define SC_TEST_GOTO_ERR(ctx, level, r, text) do { \ int _ret = (r); \ if (_ret < 0) { \ sc_do_log_color(ctx, level, FILENAME, __LINE__, __FUNCTION__, SC_COLOR_FG_RED, \ "%s: %d (%s)\n", (text), _ret, sc_strerror(_ret)); \ goto err; \ } \ } while(0) #define LOG_TEST_GOTO_ERR(ctx, r, text) SC_TEST_GOTO_ERR((ctx), SC_LOG_DEBUG_NORMAL, (r), (text)) #ifdef __cplusplus } #endif #endif OpenSC-0.26.1/src/libopensc/muscle-filesystem.c000066400000000000000000000146561474147347300213440ustar00rootroot00000000000000/* * muscle-filesystem.c: Support for MuscleCard Applet from musclecard.com * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "libopensc/muscle-filesystem.h" #include "libopensc/errors.h" #define MSCFS_NO_MEMORY SC_ERROR_OUT_OF_MEMORY #define MSCFS_INVALID_ARGS SC_ERROR_INVALID_ARGUMENTS #define MSCFS_FILE_NOT_FOUND SC_ERROR_FILE_NOT_FOUND #define MSCFS_CACHE_INCREMENT 128 static msc_id rootId = { { 0x3F, 0x00, 0x3F, 0x00 } }; static const u8* ignoredFiles[] = { (const u8*)"l0\0\0", (const u8*)"L0\0\0", NULL }; mscfs_t *mscfs_new(void) { mscfs_t *fs = malloc(sizeof(mscfs_t)); if (!fs) return NULL; memset(fs, 0, sizeof(mscfs_t)); memcpy(fs->currentPath, "\x3F\x00", 2); return fs; } void mscfs_free(mscfs_t *fs) { mscfs_clear_cache(fs); free(fs); } void mscfs_clear_cache(mscfs_t* fs) { if(!fs->cache.array) { return; } free(fs->cache.array); fs->cache.array = NULL; fs->cache.totalSize = 0; fs->cache.size = 0; } static int mscfs_is_ignored(mscfs_t* fs, msc_id objectId) { int ignored = 0; const u8** ptr = ignoredFiles; while(ptr && *ptr && !ignored) { if(0 == memcmp(objectId.id, *ptr, 4)) ignored = 1; ptr++; } 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; cache->totalSize = length; oldArray = cache->array; cache->array = malloc(sizeof(mscfs_file_t) * length); if(!cache->array) return MSCFS_NO_MEMORY; if(oldArray) { memcpy(cache->array, oldArray, sizeof(mscfs_file_t) * cache->size); free(oldArray); } } cache->array[cache->size] = *file; cache->size++; return SC_SUCCESS; } int mscfs_update_cache(mscfs_t* fs) { mscfs_file_t file; int r; mscfs_clear_cache(fs); r = fs->listFile(&file, 1, fs->udata); if(r == 0) return 0; else if(r < 0) return r; while(1) { if(!mscfs_is_ignored(fs, file.objectId)) { /* Check if its a directory in the root */ u8* oid = file.objectId.id; if(oid[2] == 0 && oid[3] == 0) { oid[2] = oid[0]; oid[3] = oid[1]; oid[0] = 0x3F; oid[1] = 0x00; file.ef = 0; } else { file.ef = 1; /* File is a working elementary file */ } r = mscfs_push_file(fs, &file); if (r != SC_SUCCESS) return r; } r = fs->listFile(&file, 0, fs->udata); if(r == 0) break; else if(r < 0) return r; } return fs->cache.size; } int mscfs_check_cache(mscfs_t* fs) { int r = SC_SUCCESS; if(!fs->cache.array) { r = mscfs_update_cache(fs); } return r; } 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 divisible by 2 */ return MSCFS_INVALID_ARGS; if(isDirectory) { /* Directory must be right next to root */ 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; } else { return MSCFS_INVALID_ARGS; } } oid[0] = fs->currentPath[0]; oid[1] = fs->currentPath[1]; /* Chop off the root in the path */ if(pathlen > 2 && memcmp(path, "\x3F\x00", 2) == 0) { path += 2; pathlen -= 2; oid[0] = 0x3F; oid[1] = 0x00; } /* Limit to a single directory */ if(pathlen > 4) return MSCFS_INVALID_ARGS; /* Reset to root */ 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 */ oid[2] = path[0]; oid[3] = path[1]; } else if(pathlen == 4) { oid[0] = path[0]; oid[1] = path[1]; oid[2] = path[2]; oid[3] = path[3]; } return 0; } int mscfs_lookup_local(mscfs_t* fs, const int id, msc_id* objectId) { u8* oid = objectId->id; oid[0] = fs->currentPath[0]; oid[1] = fs->currentPath[1]; oid[2] = (id >> 8) & 0xFF; oid[3] = id & 0xFF; return 0; } /* -1 any, 0 DF, 1 EF */ int mscfs_check_selection(mscfs_t *fs, int requiredItem) { if(fs->currentPath[0] == 0 && fs->currentPath[1] == 0) return MSCFS_INVALID_ARGS; if(requiredItem == 1 && fs->currentFile[0] == 0 && fs->currentFile[1] == 0) return MSCFS_INVALID_ARGS; return 0; } int mscfs_loadFileInfo(mscfs_t* fs, const u8 *path, size_t pathlen, mscfs_file_t **file_data, int* idx) { msc_id fullPath = {{0, 0, 0, 0}}; int x, rc; assert(fs != NULL && path != NULL && file_data != NULL); rc = mscfs_lookup_path(fs, path, pathlen, &fullPath, 0); if (rc != SC_SUCCESS) { return rc; } /* Obtain file information while checking if it exists */ rc = mscfs_check_cache(fs); if (rc < 0) return rc; if(idx) *idx = -1; for(x = 0; x < fs->cache.size; x++) { *file_data = &fs->cache.array[x]; 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; } } 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; } OpenSC-0.26.1/src/libopensc/muscle-filesystem.h000066400000000000000000000040631474147347300213400ustar00rootroot00000000000000/* * muscle-filesystem.h: Support for MuscleCard Applet from musclecard.com * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MUSCLE_FILESYSTEM_H #define MUSCLE_FILESYSTEM_H #include #include "libopensc/types.h" typedef struct msc_id { u8 id[4]; } msc_id; typedef struct mscfs_file { msc_id objectId; size_t size; unsigned short read, write, delete; int ef; int deleteFile; } mscfs_file_t; typedef struct mscfs_cache { int size; int totalSize; mscfs_file_t *array; } mscfs_cache_t; typedef struct mscsfs { u8 currentFile[2]; u8 currentPath[2]; int currentFileIndex; mscfs_cache_t cache; void* udata; int (*listFile)(mscfs_file_t *fileOut, int reset, void* udata); } mscfs_t; mscfs_t *mscfs_new(void); void mscfs_free(mscfs_t *fs); 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); int mscfs_check_cache(mscfs_t* fs); 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, size_t pathlen, mscfs_file_t **file_data, int* index); #endif OpenSC-0.26.1/src/libopensc/muscle.c000066400000000000000000000713751474147347300171630ustar00rootroot00000000000000/* * muscle.c: Support for MuscleCard Applet from musclecard.com * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "internal.h" #include "muscle.h" #define MSC_RSA_PUBLIC 0x01 #define MSC_RSA_PRIVATE 0x02 #define MSC_RSA_PRIVATE_CRT 0x03 static msc_id inputId = { { 0xFF, 0xFF, 0xFF, 0xFF } }; static msc_id outputId = { { 0xFF, 0xFF, 0xFF, 0xFE } }; int msc_list_objects(sc_card_t* card, u8 next, mscfs_file_t* file) { sc_apdu_t apdu; u8 fileData[14]; int r; sc_format_apdu(card, &apdu, SC_APDU_CASE_2, 0x58, next, 0x00); apdu.le = 14; apdu.resplen = 14; apdu.resp = fileData; r = sc_transmit_apdu(card, &apdu); if (r) return r; if(apdu.sw1 == 0x9C && apdu.sw2 == 0x12) { return 0; } r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) return r; if(apdu.resplen == 0) /* No more left */ return 0; if (apdu.resplen != 14) { sc_log(card->ctx, "expected 14 bytes, got %"SC_FORMAT_LEN_SIZE_T"u.\n", apdu.resplen); return SC_ERROR_UNKNOWN_DATA_RECEIVED; } memcpy(file->objectId.id, fileData, 4); file->size = bebytes2ulong(fileData + 4); file->read = bebytes2ushort(fileData + 8); file->write = bebytes2ushort(fileData + 10); file->delete = bebytes2ushort(fileData + 12); return 1; } int msc_partial_read_object(sc_card_t *card, msc_id objectId, int offset, u8 *data, size_t dataLength) { u8 buffer[9]; sc_apdu_t apdu; int r; sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x56, 0x00, 0x00); sc_log(card->ctx, "READ: Offset: %x\tLength: %"SC_FORMAT_LEN_SIZE_T"u\n", offset, dataLength); memcpy(buffer, objectId.id, 4); ulong2bebytes(buffer + 4, offset); buffer[8] = (u8)dataLength; apdu.data = buffer; apdu.datalen = 9; apdu.lc = 9; apdu.le = dataLength; apdu.resplen = dataLength; apdu.resp = data; r = sc_transmit_apdu(card, &apdu); 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) { SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_ALLOWED); } else if (apdu.sw2 == 0x0F) { /* GUESSED */ SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); } } sc_log(card->ctx, "got strange SWs: 0x%02X 0x%02X\n", apdu.sw1, apdu.sw2); 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 = 0; unsigned int i; size_t max_read_unit = MSC_MAX_READ; for (i = 0; i < dataLength; i += r) { r = msc_partial_read_object(card, objectId, offset + i, data + i, MIN(dataLength - i, max_read_unit)); LOG_TEST_RET(card->ctx, r, "Error in partial object read"); if (r == 0) break; } 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 = 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)); LOG_TEST_RET(card->ctx, r, "Error in zeroing file update"); } return 0; } int msc_create_object(sc_card_t *card, msc_id objectId, size_t objectSize, unsigned short readAcl, unsigned short writeAcl, unsigned short deleteAcl) { u8 buffer[14]; sc_apdu_t apdu; int r; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x5A, 0x00, 0x00); 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); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) return (int)objectSize; if(apdu.sw1 == 0x9C) { if(apdu.sw2 == 0x01) { SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_MEMORY_FAILURE); } else if(apdu.sw2 == 0x08) { SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_FILE_ALREADY_EXISTS); } else if(apdu.sw2 == 0x06) { SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_ALLOWED); } } if (card->ctx->debug >= 2) { sc_log(card->ctx, "got strange SWs: 0x%02X 0x%02X\n", apdu.sw1, apdu.sw2); } msc_zero_object(card, objectId, objectSize); return (int)objectSize; } /* Update up to MSC_MAX_READ - 9 bytes */ 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_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; memcpy(buffer + 9, data, dataLength); apdu.data = buffer; apdu.datalen = apdu.lc; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) 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) { SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_ALLOWED); } else if(apdu.sw2 == 0x0F) { /* GUESSED */ SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); } } if (card->ctx->debug >= 2) { sc_log(card->ctx, "got strange SWs: 0x%02X 0x%02X\n", apdu.sw1, apdu.sw2); } return (int)dataLength; } int msc_update_object(sc_card_t *card, msc_id objectId, int offset, const u8 *data, size_t dataLength) { int r; size_t i; 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)); LOG_TEST_RET(card->ctx, r, "Error in partial object update"); } return (int)dataLength; } int msc_delete_object(sc_card_t *card, msc_id objectId, int zero) { sc_apdu_t apdu; int r; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x52, 0x00, zero ? 0x01 : 0x00); apdu.lc = 4; apdu.data = objectId.id; apdu.datalen = 4; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) return 0; 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) { SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_ALLOWED); } } if (card->ctx->debug >= 2) { sc_log(card->ctx, "got strange SWs: 0x%02X 0x%02X\n", apdu.sw1, apdu.sw2); } return 0; } int msc_select_applet(sc_card_t *card, u8 *appletId, size_t appletIdLength) { sc_apdu_t apdu; int r; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 4, 0); apdu.lc = appletIdLength; apdu.data = appletId; apdu.datalen = appletIdLength; apdu.resplen = 0; apdu.le = 0; r = sc_transmit_apdu(card, &apdu); 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, size_t *pinLength) { for(; *pinLength > 0; (*pinLength)--) { if(pin[*pinLength - 1]) break; } } int msc_verify_pin(sc_card_t *card, int pinNumber, const u8 *pinValue, int pinLength, int *tries) { sc_apdu_t apdu; int r; const int bufferLength = MSC_MAX_PIN_LENGTH; u8 buffer[MSC_MAX_PIN_LENGTH]; 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); 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; LOG_FUNC_RETURN(card->ctx, SC_ERROR_PIN_CODE_INCORRECT); } else if(apdu.sw1 == 0x9C && apdu.sw2 == 0x02) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_PIN_CODE_INCORRECT); } else if(apdu.sw1 == 0x69 && apdu.sw2 == 0x83) { 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 */ 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) { if (!buffer || bufferLength < (size_t)pinLength || pinLength > MSC_MAX_PIN_LENGTH) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); truncatePinNulls(pinValue, &pinLength); memcpy(buffer, pinValue, pinLength); sc_format_apdu(card, apdu, SC_APDU_CASE_3_SHORT, 0x42, pinNumber, 0); 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) { sc_apdu_t apdu; int r; const int bufferLength = MSC_MAX_PIN_LENGTH; u8 buffer[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"); if(tries) *tries = -1; r = sc_transmit_apdu(card, &apdu); 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; LOG_FUNC_RETURN(card->ctx, SC_ERROR_PIN_CODE_INCORRECT); } else if(apdu.sw1 == 0x9C && apdu.sw2 == 0x02) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_PIN_CODE_INCORRECT); } else if(apdu.sw1 == 0x69 && apdu.sw2 == 0x83) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_AUTH_METHOD_BLOCKED); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_PIN_CODE_INCORRECT); } 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) { if (!buffer || bufferLength < (size_t)pukLength || pukLength > MSC_MAX_PIN_LENGTH) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); truncatePinNulls(pukValue, &pukLength); memcpy(buffer, pukValue, pukLength); sc_format_apdu(card, apdu, SC_APDU_CASE_3_SHORT, 0x46, pinNumber, 0); 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) { sc_apdu_t apdu; int r; const int bufferLength = (MSC_MAX_PIN_LENGTH + 1) * 2; u8 buffer[(MSC_MAX_PIN_LENGTH + 1) * 2]; 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); 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; LOG_FUNC_RETURN(card->ctx, SC_ERROR_PIN_CODE_INCORRECT); } else if(apdu.sw1 == 0x9C && apdu.sw2 == 0x02) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_PIN_CODE_INCORRECT); } else if(apdu.sw1 == 0x69 && apdu.sw2 == 0x83) { 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 */ 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; 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); ptr = buffer; sc_format_apdu(card, apdu, SC_APDU_CASE_3_SHORT, 0x44, pinNumber, 0); *ptr = pinLength; ptr++; memcpy(ptr, pinValue, pinLength); ptr += pinLength; *ptr = newPinLength; ptr++; memcpy(ptr, newPin, newPinLength); 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) { sc_apdu_t apdu; 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; 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) LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); ptr = buffer; ushort2bebytes(ptr, dataLength); ptr+=2; ushort2bebytes(ptr, seedLength); ptr+=2; if(seedLength > 0) { memcpy(ptr, seedData, seedLength); } sc_format_apdu(card, &apdu, cse, 0x62, 0x00, location); apdu.data = buffer; apdu.datalen = len; apdu.lc = len; if(location == 1) { u8* outputBuffer = malloc(dataLength + 2); if(outputBuffer == NULL) LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); apdu.le = dataLength + 2; apdu.resp = outputBuffer; apdu.resplen = dataLength + 2; } r = sc_transmit_apdu(card, &apdu); if(location == 1) { memcpy(outputData, apdu.resp + 2, dataLength); free(apdu.resp); } free(buffer); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if(location == 1) { if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { return SC_SUCCESS; } else { r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) { if (card->ctx->debug >= 2) { sc_log(card->ctx, "got strange SWs: 0x%02X 0x%02X\n", apdu.sw1, apdu.sw2); } LOG_FUNC_RETURN(card->ctx, r); } 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_log(card->ctx, "got strange SWs: 0x%02X 0x%02X\n", apdu.sw1, apdu.sw2); } LOG_FUNC_RETURN(card->ctx, r); } LOG_FUNC_RETURN(card->ctx, SC_ERROR_CARD_CMD_FAILED); } r = msc_read_object(card, inputId, 2, outputData, dataLength); if(r < 0) LOG_FUNC_RETURN(card->ctx, r); msc_delete_object(card, inputId,0); LOG_FUNC_RETURN(card->ctx, r); } } 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]; /* Key pair payload length */ u8 *ptr = buffer; int r; unsigned short prRead = 0xFFFF, prWrite = 0x0002, prCompute = 0x0002, puRead = 0x0000, puWrite = 0x0002, puCompute = 0x0000; 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); 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_log(card->ctx, "got strange SWs: 0x%02X 0x%02X\n", apdu.sw1, apdu.sw2); } LOG_FUNC_RETURN(card->ctx, r); } LOG_FUNC_RETURN(card->ctx, SC_ERROR_CARD_CMD_FAILED); } int msc_extract_key(sc_card_t *card, int keyLocation) { sc_apdu_t apdu; u8 encoding = 0; int r; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x34, keyLocation, 0x00); apdu.data = &encoding; apdu.datalen = 1; apdu.lc = 1; r = sc_transmit_apdu(card, &apdu); 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_log(card->ctx, "got strange SWs: 0x%02X 0x%02X\n", apdu.sw1, apdu.sw2); } LOG_FUNC_RETURN(card->ctx, r); } LOG_FUNC_RETURN(card->ctx, SC_ERROR_CARD_CMD_FAILED); } int msc_extract_rsa_public_key(sc_card_t *card, int keyLocation, size_t* modLength, u8** modulus, size_t* expLength, u8** exponent) { int r; u8 buffer[1024]; /* Should be plenty... */ int fileLocation = 1; r = msc_extract_key(card, keyLocation); 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) 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) 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) LOG_FUNC_RETURN(card->ctx, r); *modulus = malloc(*modLength); 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; LOG_FUNC_RETURN(card->ctx, r); } *exponent = malloc(*expLength); if(!*exponent) { free(*modulus); LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); } memcpy(*exponent, buffer, *expLength); return 0; } /* 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 keyLocation, int cipherMode, int cipherDirection, const u8* initData, u8* outputData, size_t dataLength, size_t* outputDataLength) { sc_apdu_t apdu; u8 buffer[MSC_MAX_APDU]; u8 *ptr; int r; u8 outputBuffer[MSC_MAX_APDU + 2]; sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x36, keyLocation, 0x01); /* Init */ apdu.data = buffer; apdu.datalen = dataLength + 5; apdu.lc = dataLength + 5; memset(outputBuffer, 0, sizeof(outputBuffer)); apdu.resp = outputBuffer; apdu.resplen = dataLength + 2; apdu.le = dataLength + 2; ptr = buffer; *ptr = cipherMode; ptr++; *ptr = cipherDirection; ptr++; *ptr = 0x01; ptr++; /* DATA LOCATION: APDU */ *ptr = (dataLength >> 8) & 0xFF; ptr++; *ptr = dataLength & 0xFF; ptr++; memcpy(ptr, initData, dataLength); r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { short receivedData = outputBuffer[0] << 8 | outputBuffer[1]; *outputDataLength = receivedData; if (receivedData > MSC_MAX_APDU) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_DATA); memcpy(outputData, outputBuffer + 2, receivedData); return 0; } r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) { if (card->ctx->debug >= 2) { sc_log(card->ctx, "init: got strange SWs: 0x%02X 0x%02X\n", apdu.sw1, apdu.sw2); } LOG_FUNC_RETURN(card->ctx, r); } LOG_FUNC_RETURN(card->ctx, SC_ERROR_CARD_CMD_FAILED); } int msc_compute_crypt_final( sc_card_t *card, int keyLocation, const u8* inputData, u8* outputData, size_t dataLength, size_t* outputDataLength) { sc_apdu_t apdu; u8 buffer[MSC_MAX_APDU]; u8 outputBuffer[MSC_MAX_APDU + 2]; u8 *ptr; 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; apdu.le = dataLength +2; ptr = buffer; *ptr = 0x01; ptr++; /* DATA LOCATION: APDU */ *ptr = (dataLength >> 8) & 0xFF; ptr++; *ptr = dataLength & 0xFF; ptr++; memcpy(ptr, inputData, dataLength); r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { short receivedData = outputBuffer[0] << 8 | outputBuffer[1]; *outputDataLength = receivedData; if (receivedData > MSC_MAX_APDU) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_DATA); memcpy(outputData, outputBuffer + 2, receivedData); return 0; } r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) { if (card->ctx->debug >= 2) { sc_log(card->ctx, "final: got strange SWs: 0x%02X 0x%02X\n", apdu.sw1, apdu.sw2); } LOG_FUNC_RETURN(card->ctx, r); } 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, int keyLocation, const u8* inputData, u8* outputData, size_t dataLength, size_t* outputDataLength) { sc_apdu_t apdu; u8 buffer[MSC_MAX_APDU]; u8 *ptr; 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 */ *ptr = (dataLength >> 8) & 0xFF; ptr++; *ptr = dataLength & 0xFF; ptr++; memcpy(ptr, inputData, dataLength); r = msc_create_object(card, outputId, dataLength + 2, 0x02, 0x02, 0x02); if(r < 0) { if(r == SC_ERROR_FILE_ALREADY_EXISTS) { r = msc_delete_object(card, outputId, 0); if(r < 0) { SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } r = msc_create_object(card, outputId, dataLength + 2, 0x02, 0x02, 0x02); if(r < 0) { SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } } } r = msc_update_object(card, outputId, 0, buffer + 1, dataLength + 2); if(r < 0) return r; r = sc_transmit_apdu(card, &apdu); 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); if (r >= 0) *outputDataLength = r; msc_delete_object(card, outputId, 0); msc_delete_object(card, inputId, 0); return r; } r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) { if (card->ctx->debug >= 2) { 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 */ msc_delete_object(card, outputId, 0); LOG_FUNC_RETURN(card->ctx, r); } int msc_compute_crypt(sc_card_t *card, int keyLocation, int cipherMode, int cipherDirection, const u8* data, u8* outputData, size_t dataLength, size_t outputDataLength) { size_t left = dataLength; const u8* inPtr = data; u8* outPtr = outputData; int toSend; int r; size_t received = 0; 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, toSend, &received); if(r < 0) LOG_FUNC_RETURN(card->ctx, r); left -= toSend; inPtr += toSend; outPtr += received; 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) { r = msc_compute_crypt_final(card, keyLocation, inPtr, outPtr, toSend, &received); 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) LOG_FUNC_RETURN(card->ctx, r); } outPtr += received; return (int)(outPtr - outputData); /* Amt received */ } /* USED IN KEY ITEM WRITING */ #define CPYVAL(valName) \ ushort2bebytes(p, data->valName ## Length); p+= 2; \ memcpy(p, data->valName ## Value, data->valName ## Length); p+= data->valName ## Length int msc_import_key(sc_card_t *card, int keyLocation, sc_cardctl_muscle_key_info_t *data) { unsigned short readAcl = 0xFFFF, writeAcl = 0x0002, use = 0x0002, keySize = data->keySize; size_t bufferSize = 0; u8 *buffer, *p; u8 apduBuffer[6]; sc_apdu_t apdu; int r; 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); } 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); } 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) { bufferSize = 4 + 10 + data->pLength + data->qLength + data->pqLength + data->dp1Length + data->dq1Length; } buffer = malloc(bufferSize); 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); } else if(data->keyType == 0x03) { CPYVAL(p); CPYVAL(q); CPYVAL(pq); CPYVAL(dp1); CPYVAL(dq1); } r = msc_create_object(card, outputId, bufferSize, 0x02, 0x02, 0x02); if(r < 0) { if(r == SC_ERROR_FILE_ALREADY_EXISTS) { r = msc_delete_object(card, outputId, 0); if(r < 0) { free(buffer); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } r = msc_create_object(card, outputId, bufferSize, 0x02, 0x02, 0x02); if(r < 0) { free(buffer); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } } } 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; apdu.datalen = 6; p = apduBuffer; ushort2bebytes(p, readAcl); p+=2; ushort2bebytes(p, writeAcl); p+=2; ushort2bebytes(p, use); r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { msc_delete_object(card, outputId, 0); return 0; } r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) { if (card->ctx->debug >= 2) { 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); LOG_FUNC_RETURN(card->ctx, r); } /* this is last ditch cleanup */ msc_delete_object(card, outputId, 0); LOG_FUNC_RETURN(card->ctx, SC_ERROR_CARD_CMD_FAILED); } #undef CPYVAL OpenSC-0.26.1/src/libopensc/muscle.h000066400000000000000000000105111474147347300171510ustar00rootroot00000000000000/* * muscle.h: Support for MuscleCard Applet from musclecard.com * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MUSCLE_H_ #define MUSCLE_H_ #include #include "libopensc/types.h" #include "libopensc/opensc.h" #include "libopensc/cardctl.h" #include "libopensc/muscle-filesystem.h" #define MSC_MAX_APDU 512 /* Max APDU send/recv, used for stack allocation */ #define MSC_MAX_PIN_LENGTH 8 #define MSC_MAX_PIN_COMMAND_LENGTH ((1 + MSC_MAX_PIN_LENGTH) * 2) /* Currently max size handled by muscle driver is 255 ... */ #define MSC_MAX_READ (card->max_recv_size > 0 ? card->max_recv_size : 255) #define MSC_MAX_SEND (card->max_send_size > 0 ? card->max_send_size : 255) 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, 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); 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); 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); 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); 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, size_t keySize, int options); int msc_extract_rsa_public_key(sc_card_t *card, int keyLocation, size_t* modLength, u8** modulus, size_t* expLength, u8** exponent); int msc_extract_key(sc_card_t *card, int keyLocation); int msc_compute_crypt_init(sc_card_t *card, int keyLocation, int cipherMode, int cipherDirection, const u8* initData, u8* outputData, size_t dataLength, size_t* outputDataLength); int msc_compute_crypt_process( 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, int keyLocation, const u8* inputData, u8* outputData, size_t dataLength, size_t* outputDataLength); int msc_compute_crypt(sc_card_t *card, int keyLocation, int cipherMode, int cipherDirection, const u8* data, u8* outputData, size_t dataLength, size_t outputDataLength); int msc_import_key(sc_card_t *card, int keyLocation, sc_cardctl_muscle_key_info_t *data); #endif /*MUSCLE_H_*/ OpenSC-0.26.1/src/libopensc/opensc.dll.manifest000066400000000000000000000010041474147347300212760ustar00rootroot00000000000000 OpenSC-0.26.1/src/libopensc/opensc.h000066400000000000000000001771101474147347300171610ustar00rootroot00000000000000/* * opensc.h: OpenSC library header file * * Copyright (C) 2001, 2002 Juha Yrjölä * 2005 The OpenSC project * * 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 */ /** * @file src/libopensc/opensc.h * OpenSC library core header file */ #ifndef _OPENSC_H #define _OPENSC_H #include #ifdef HAVE_UNISTD_H #include #endif #ifdef __cplusplus extern "C" { #endif #include "common/simclist.h" #include "scconf/scconf.h" #include "libopensc/errors.h" #include "libopensc/types.h" #ifdef ENABLE_SM #include "libopensc/sm.h" #endif #if defined(_WIN32) && !(defined(__MINGW32__) && defined (__MINGW_PRINTF_FORMAT)) #define SC_FORMAT_LEN_SIZE_T "I" #define SC_FORMAT_LEN_PTRDIFF_T "I" #else /* hope SUSv3 ones work */ #define SC_FORMAT_LEN_SIZE_T "z" #define SC_FORMAT_LEN_PTRDIFF_T "t" #endif #define SC_SEC_OPERATION_DECIPHER 0x0001 #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 #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_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 #define SC_ALGORITHM_SHA1 129 #define SC_ALGORITHM_GOSTR3411 130 /* Key derivation algorithms */ #define SC_ALGORITHM_PBKDF2 192 /* 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 0x001FFFFF /* If the card is willing to produce a cryptogram padded with the following * 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. 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_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 #define SC_ALGORITHM_ECDSA_HASH_SHA256 SC_ALGORITHM_RSA_HASH_SHA256 #define SC_ALGORITHM_ECDSA_HASH_SHA384 SC_ALGORITHM_RSA_HASH_SHA384 #define SC_ALGORITHM_ECDSA_HASH_SHA512 SC_ALGORITHM_RSA_HASH_SHA512 #define SC_ALGORITHM_ECDSA_HASHES (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) /* 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_ECDH_CDH_RAW | \ SC_ALGORITHM_ECDSA_RAW) /* 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 #define SC_ALGORITHM_EXT_EC_NAMEDCURVE 0x00000008 #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_READER_ATTACHED 0x0004 #define SC_EVENT_READER_DETACHED 0x0008 #define SC_EVENT_READER_EVENTS (SC_EVENT_READER_ATTACHED|SC_EVENT_READER_DETACHED) #define MAX_FILE_SIZE 65535 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 long algorithm, algorithm_flags; size_t key_size_bits; unsigned long algorithm_ref; struct sc_path file_ref; 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 long algorithm; struct sc_object_id oid; void *params; }; struct sc_pbkdf2_params { u8 salt[16]; size_t salt_len; int iterations; size_t key_length; struct sc_algorithm_id hash_alg; }; struct sc_pbes2_params { struct sc_algorithm_id derivation_alg; struct sc_algorithm_id key_encr_alg; }; /* * The ecParameters can be presented as * - name of curve; * - OID of named curve; * - implicit parameters. * * type - type(choice) of 'EC domain parameters' as it present in CKA_EC_PARAMS (PKCS#11). Recommended value '1' -- namedCurve. * field_length - EC key size in bits. */ struct sc_ec_parameters { char *named_curve; struct sc_object_id id; struct sc_lv_data der; int type; size_t field_length; }; typedef struct sc_algorithm_info { unsigned int algorithm; size_t key_length; unsigned long flags; union { struct sc_rsa_info { unsigned long exponent; } _rsa; struct sc_ec_info { unsigned long ext_flags; struct sc_ec_parameters params; } _ec; } u; } sc_algorithm_info_t; typedef struct sc_app_info { char *label; struct sc_aid aid; struct sc_ddo ddo; struct sc_path path; int rec_nr; /* -1, if EF(DIR) is transparent */ } sc_app_info_t; struct sc_ef_atr { unsigned char card_service; unsigned char df_selection; size_t unit_size; unsigned char card_capabilities; size_t max_command_apdu; size_t max_response_apdu; struct sc_aid aid; unsigned char pre_issuing[6]; size_t pre_issuing_len; unsigned char issuer_data[16]; size_t issuer_data_len; struct sc_object_id allocation_oid; 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 #define SC_PROTO_ANY 0xFFFFFFFF struct sc_reader_driver { const char *name; const char *short_name; struct sc_reader_operations *ops; void *dll; }; /* reader flags */ #define SC_READER_CARD_PRESENT 0x00000001 #define SC_READER_CARD_CHANGED 0x00000002 #define SC_READER_CARD_INUSE 0x00000004 #define SC_READER_CARD_EXCLUSIVE 0x00000008 #define SC_READER_HAS_WAITING_AREA 0x00000010 #define SC_READER_REMOVED 0x00000020 #define SC_READER_ENABLE_ESCAPE 0x00000040 /* reader capabilities */ #define SC_READER_CAP_DISPLAY 0x00000001 #define SC_READER_CAP_PIN_PAD 0x00000002 #define SC_READER_CAP_PACE_EID 0x00000004 #define SC_READER_CAP_PACE_ESIGN 0x00000008 #define SC_READER_CAP_PACE_DESTROY_CHANNEL 0x00000010 #define SC_READER_CAP_PACE_GENERIC 0x00000020 /* reader send/receive length of short APDU */ #define SC_READER_SHORT_APDU_MAX_SEND_SIZE 255 #define SC_READER_SHORT_APDU_MAX_RECV_SIZE 256 typedef struct sc_reader { struct sc_context *ctx; const struct sc_reader_driver *driver; const struct sc_reader_operations *ops; void *drv_data; char *name; char *vendor; unsigned char version_major; unsigned char version_minor; 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 */ struct sc_atr atr; struct sc_uid uid; struct _atr_info { u8 *hist_bytes; size_t hist_bytes_len; int Fi, f, Di, N; u8 FI, DI; } atr_info; } sc_reader_t; /* This will be the new interface for handling PIN commands. * It is supposed to support pin pads (with or without display) * attached to the reader. */ #define SC_PIN_CMD_VERIFY 0 #define SC_PIN_CMD_CHANGE 1 #define SC_PIN_CMD_UNBLOCK 2 #define SC_PIN_CMD_GET_INFO 3 #define SC_PIN_CMD_GET_SESSION_PIN 4 #define SC_PIN_CMD_USE_PINPAD 0x0001 #define SC_PIN_CMD_NEED_PADDING 0x0002 #define SC_PIN_CMD_IMPLICIT_CHANGE 0x0004 #define SC_PIN_ENCODING_ASCII 0 #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 /* 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, 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 */ unsigned int encoding; /* ASCII-numeric, BCD, etc */ 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 when using pin pad */ 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; struct sc_apdu *apdu; /* APDU of the PIN command */ }; struct sc_reader_operations { /* Called during sc_establish_context(), when the driver * is loaded */ int (*init)(struct sc_context *ctx); /* Called when the driver is being unloaded. finish() has to * release any resources. */ int (*finish)(struct sc_context *ctx); /* Called when library wish to detect new readers * should add only new readers. */ int (*detect_readers)(struct sc_context *ctx); int (*cancel)(struct sc_context *ctx); /* Called when releasing a reader. release() has to * deallocate the private data. Other fields will be * freed by OpenSC. */ int (*release)(struct sc_reader *reader); int (*detect_card_presence)(struct sc_reader *reader); int (*connect)(struct sc_reader *reader); int (*disconnect)(struct sc_reader *reader); int (*transmit)(struct sc_reader *reader, sc_apdu_t *apdu); int (*lock)(struct sc_reader *reader); int (*unlock)(struct sc_reader *reader); int (*set_protocol)(struct sc_reader *reader, unsigned int proto); /* Pin pad functions */ int (*display_message)(struct sc_reader *, const char *); int (*perform_verify)(struct sc_reader *, struct sc_pin_cmd_data *); int (*perform_pace)(struct sc_reader *reader, void *establish_pace_channel_input, void *establish_pace_channel_output); /* Wait for an event */ int (*wait_for_event)(struct sc_context *ctx, unsigned int event_mask, sc_reader_t **event_reader, unsigned int *event, int timeout, void **reader_states); /* Reset a reader */ int (*reset)(struct sc_reader *, int); /* Used to pass in PC/SC handles to minidriver */ int (*use_reader)(struct sc_context *ctx, void *pcsc_context_handle, void *pcsc_card_handle); }; /* * Card flags * * Used to hint about card specific capabilities and algorithms * supported to the card driver. Used in sc_atr_table and * card_atr block structures in the configuration file. * * Unknown, card vendor specific values may exists, but must * not conflict with values defined here. All actions defined * by the flags must be handled by the card driver themselves. */ /* Mask for card vendor specific values */ #define SC_CARD_FLAG_VENDOR_MASK 0xFFFF0000 /* Hint SC_CARD_CAP_RNG */ #define SC_CARD_FLAG_RNG 0x00000002 #define SC_CARD_FLAG_KEEP_ALIVE 0x00000004 /* * Card capabilities */ /* Card can handle large (> 256 bytes) buffers in calls to * read_binary, write_binary and update_binary; if not, * several successive calls to the corresponding function * is made. */ #define SC_CARD_CAP_APDU_EXT 0x00000001 /* Card has on-board random number source. */ #define SC_CARD_CAP_RNG 0x00000004 /* Card supports ISO7816 PIN status queries using an empty VERIFY */ #define SC_CARD_CAP_ISO7816_PIN_INFO 0x00000008 /* Use the card's ACs in sc_pkcs15init_authenticate(), * instead of relying on the ACL info in the profile files. */ #define SC_CARD_CAP_USE_FCI_AC 0x00000010 /* 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; struct sc_atr atr; struct sc_uid uid; int type; /* Card type, for card driver internal use */ unsigned long caps, flags; int cla; size_t max_send_size; /* Max Lc supported by the card */ size_t max_recv_size; /* Max Le supported by the card */ struct sc_app_info *app[SC_MAX_CARD_APPS]; int app_count; struct sc_ef_atr *ef_atr; struct sc_algorithm_info *algorithms; int algorithm_count; int lock_count; struct sc_card_driver *driver; struct sc_card_operations *ops; const char *name; void *drv_data; int max_pin_len; struct sc_card_cache cache; struct sc_serial_number serialnr; struct sc_version version; void *mutex; #ifdef ENABLE_SM struct sm_context sm_ctx; #endif unsigned int magic; } sc_card_t; struct sc_card_operations { /** @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. It is recommended not to modify `card` during this call. * */ int (*match_card)(struct sc_card *card); /** @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. drv_data may be used to * store card driver's (allocated) private data. */ int (*init)(struct sc_card *card); /** @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); /** * @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, 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, unsigned int idx, const u8 * buf, size_t count, unsigned long flags); /* select_file: Does the equivalent of SELECT FILE command specified * in ISO7816-4. Stores information about the selected file to * , if not NULL. */ 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); /* * ISO 7816-8 functions */ /* verify: Verifies reference data of type , identified by * . If is not NULL, number of verifying * tries left is saved in case of verification failure, if the * information is available. */ int (*verify)(struct sc_card *card, unsigned int type, int ref_qualifier, const u8 *data, size_t data_len, int *tries_left); /* logout: Resets all access rights that were gained. */ int (*logout)(struct sc_card *card); /* restore_security_env: Restores a previously saved security * environment, and stores information about the environment to * , if not NULL. */ int (*restore_security_env)(struct sc_card *card, int se_num); /* set_security_env: 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. */ int (*set_security_env)(struct sc_card *card, const struct sc_security_env *env, int se_num); /* decipher: Engages the deciphering operation. Card will use the * security environment set in a call to set_security_env or * restore_security_env. */ 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. Similar * to the function decipher. */ int (*compute_signature)(struct sc_card *card, const u8 * data, size_t data_len, u8 * out, size_t outlen); int (*change_reference_data)(struct sc_card *card, unsigned int type, int ref_qualifier, const u8 *old, size_t oldlen, const u8 *newref, size_t newlen, int *tries_left); int (*reset_retry_counter)(struct sc_card *card, unsigned int type, int ref_qualifier, const u8 *puk, size_t puklen, const u8 *newref, size_t newlen); /* * ISO 7816-9 functions */ int (*create_file)(struct sc_card *card, struct sc_file *file); int (*delete_file)(struct sc_card *card, const struct sc_path *path); /* list_files: Enumerates all the files in the current DF, and * writes the corresponding file identifiers to . Returns * the number of bytes stored. */ int (*list_files)(struct sc_card *card, u8 *buf, size_t buflen); int (*check_sw)(struct sc_card *card,unsigned int sw1,unsigned int sw2); int (*card_ctl)(struct sc_card *card, unsigned long request, void *data); int (*process_fci)(struct sc_card *card, struct sc_file *file, const u8 *buf, size_t buflen); int (*construct_fci)(struct sc_card *card, const struct sc_file *file, u8 *out, size_t *outlen); /* 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 (*get_data)(struct sc_card *, unsigned int, u8 *, size_t); int (*put_data)(struct sc_card *, unsigned int, const u8 *, size_t); int (*delete_record)(struct sc_card *card, unsigned int rec_nr); int (*read_public_key)(struct sc_card *, unsigned, struct sc_path *, unsigned, unsigned, 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 { const char *name; const char *short_name; struct sc_card_operations *ops; struct sc_atr_table *atr_map; unsigned int natrs; void *dll; } sc_card_driver_t; /** * @struct sc_thread_context_t * Structure for the locking function to use when using libopensc * in a multi-threaded application. */ typedef struct { /** the version number of this structure (0 for this version) */ unsigned int ver; /** creates a mutex object */ int (*create_mutex)(void **); /** locks a mutex object (blocks until the lock has been acquired) */ int (*lock_mutex)(void *); /** unlocks a mutex object */ int (*unlock_mutex)(void *); /** destroys a mutex object */ int (*destroy_mutex)(void *); /** returns unique identifier for the thread (can be NULL) */ unsigned long (*thread_id)(void); } sc_thread_context_t; /** 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 * the duplicated card handles for the parent process. With this flag the child * process indicates that shall the reader shall ignore those resources when * 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[4]; char *app_name; char *exe_path; int debug; unsigned long flags; FILE *debug_file; char *debug_filename; char *preferred_language; list_t readers; struct sc_reader_driver *reader_driver; void *reader_drv_data; struct sc_card_driver *card_drivers[SC_MAX_CARD_DRIVERS]; struct sc_card_driver *forced_driver; sc_thread_context_t *thread_ctx; void *mutex; #ifdef ENABLE_OPENSSL ossl3ctx_t *ossl3ctx; #endif unsigned int magic; int disable_hw_pkcs1_padding; } sc_context_t; /* APDU handling functions */ /** 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 success and an error code otherwise */ int sc_transmit_apdu(struct sc_card *card, struct sc_apdu *apdu); 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 *); /** Transforms an APDU from binary to its @c sc_apdu_t representation * @param ctx sc_context_t object (used for logging) * @param buf APDU to be encoded as an @c sc_apdu_t object * @param len length of @a buf * @param apdu @c sc_apdu_t object to initialize * @return SC_SUCCESS on success and an error code otherwise * @note On successful initialization apdu->data will point to @a buf with an * appropriate offset. Only free() @a buf, when apdu->data is not needed any * longer. * @note On successful initialization @a apdu->resp and apdu->resplen will be * 0. You should modify both if you are expecting data in the response 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); /********************************************************************/ /* opensc context functions */ /********************************************************************/ /** * Establishes an OpenSC context. Note: this function is deprecated, * please use sc_context_create() instead. * @param ctx A pointer to a pointer that will receive the allocated context * @param app_name A string that identifies the application, used primarily * in finding application-specific configuration data. Can be NULL. */ int sc_establish_context(sc_context_t **ctx, const char *app_name); /** * @struct sc_context_t initialization parameters * Structure to supply additional parameters, for example * mutex information, to the sc_context_t creation. */ typedef struct { /** version number of this structure (0 for this version) */ unsigned int ver; /** name of the application (used for finding application * 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; /** * Repairs an already existing sc_context_t object. This may occur if * multithreaded issues mean that another context in the same heap is deleted. * @param ctx pointer to a sc_context_t pointer containing the (partial) * context. * @return SC_SUCCESS or an error value if an error occurred. */ int sc_context_repair(sc_context_t **ctx); /** * Creates a new sc_context_t object. * @param ctx pointer to a sc_context_t pointer for the newly * created sc_context_t object. * @param parm parameters for the sc_context_t creation (see * sc_context_param_t for a description of the supported * options).. * @return SC_SUCCESS on success and an error code otherwise. */ int sc_context_create(sc_context_t **ctx, const sc_context_param_t *parm); /** * Releases an established OpenSC context * @param ctx A pointer to the context structure to be released */ int sc_release_context(sc_context_t *ctx); /** * Detect new readers available on system. * @param ctx OpenSC context * @return SC_SUCCESS on success and an error code otherwise. */ int sc_ctx_detect_readers(sc_context_t *ctx); /** * In windows: get configuration option from environment or from registers. * @param env name of environment variable * @param reg name of register value * @param key path of register key * @return SC_SUCCESS on success and an error code otherwise. */ 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 * @param ctx OpenSC context * @param i number of the reader structure to return (starting with 0) * @return the requested sc_reader object or NULL if the index is * not available */ sc_reader_t *sc_ctx_get_reader(sc_context_t *ctx, unsigned int i); /** * Pass in pointers to handles to be used for the pcsc reader. * This is used by cardmod to pass in handles provided by BaseCSP * * @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 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 * @param name name of the reader to look for * @return the requested sc_reader object or NULL if the reader is * not available */ sc_reader_t *sc_ctx_get_reader_by_name(sc_context_t *ctx, const char *name); /** * Returns a pointer to the specified sc_reader_t object * @param ctx OpenSC context * @param id id of the reader (starting from 0) * @return the requested sc_reader object or NULL if the reader is * not available */ sc_reader_t *sc_ctx_get_reader_by_id(sc_context_t *ctx, unsigned int id); /** * Returns the number a available sc_reader objects * @param ctx OpenSC context * @return the number of available reader objects */ unsigned int sc_ctx_get_reader_count(sc_context_t *ctx); int _sc_delete_reader(sc_context_t *ctx, sc_reader_t *reader); /** * Redirects OpenSC debug log to the specified file * @param ctx existing OpenSC context * @param filename path to the file or "stderr" or "stdout" * @return SC_SUCCESS on success and an error code otherwise */ int sc_ctx_log_to_file(sc_context_t *ctx, const char* filename); /** * Forces the use of a specified card driver * @param ctx OpenSC context * @param short_name The short name of the driver to use (e.g. 'cardos') */ int sc_set_card_driver(sc_context_t *ctx, const char *short_name); /** * Connects to a card in a reader and auto-detects the card driver. * The ATR (Answer to Reset) string of the card is also retrieved. * @param reader Reader structure * @param card The allocated card object will go here */ int sc_connect_card(sc_reader_t *reader, struct sc_card **card); /** * Disconnects from a card, and frees the card structure. Any locks * made by the application must be released before calling this function. * NOTE: The card is not reset nor powered down after the operation. * @param card The card to disconnect * @return SC_SUCCESS on success and an error code otherwise */ 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 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 * always set. In addition, if the card was exchanged, * the SC_READER_CARD_CHANGED flag is set. */ int sc_detect_card_presence(sc_reader_t *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 constants listed above. * @param timeout Amount of millisecs to wait; -1 means forever * @retval < 0 if an error occurred * @retval = 0 if a an event happened * @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, int timeout, void **reader_states); /** * Resets the card. * NOTE: only PC/SC backend implements this function at this moment. * @param card The card to reset. * @param do_cold_reset 0 for a warm reset, 1 for a cold reset (unpower) * @retval SC_SUCCESS on success */ int sc_reset(struct sc_card *card, int do_cold_reset); /** * Cancel all pending PC/SC calls * NOTE: only PC/SC backend implements this function. * @param ctx pointer to application context * @retval SC_SUCCESS on success */ int sc_cancel(sc_context_t *ctx); /** * Tries acquire the reader lock. * @param card The card to lock * @retval SC_SUCCESS on success */ int sc_lock(struct sc_card *card); /** * Unlocks a previously acquired reader lock. * @param card The card to unlock * @retval SC_SUCCESS on success */ int sc_unlock(struct sc_card *card); /** * @brief Calculate the maximum size of R-APDU payload (Ne). * * Takes card limitations into account such as extended length support as well * as the reader's limitation for data transfer. * * @param card Initialized card object with its reader * * @return maximum Ne */ size_t sc_get_max_recv_size(const sc_card_t *card); /** * @brief Calculate the maximum size of C-APDU payload (Nc). * * Takes card limitations into account such as extended length support as well * as the reader's limitation for data transfer. * * @param card card * * @return maximum Nc */ size_t sc_get_max_send_size(const sc_card_t *card); /********************************************************************/ /* ISO 7816-4 related functions */ /********************************************************************/ /** * Does the equivalent of ISO 7816-4 command SELECT FILE. * @param card struct sc_card object on which to issue the command * @param path The path, file id or name of the desired file * @param file If not NULL, will receive a pointer to a new structure * @return SC_SUCCESS on success and an error code otherwise */ int sc_select_file(struct sc_card *card, const sc_path_t *path, sc_file_t **file); /** * List file ids within a DF * @param card struct sc_card object on which to issue the command * @param buf buffer for the read file ids (the filed ids are * stored in the buffer as a sequence of 2 byte values) * @param buflen length of the supplied buffer * @return number of files ids read or an error code */ int sc_list_files(struct sc_card *card, u8 *buf, size_t buflen); /** * @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 * @param count number of bytes to read * @param flags flags for the READ BINARY command (currently not used) * @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); /** * @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 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); /** * @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 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 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 erased or an error code */ int sc_erase_binary(struct sc_card *card, unsigned int idx, size_t count, unsigned long flags); #define SC_RECORD_EF_ID_MASK 0x0001FUL /** flags for record operations */ /** use first record */ #define SC_RECORD_BY_REC_ID 0x00000UL /** use the specified record number */ #define SC_RECORD_BY_REC_NR 0x00100UL /** use currently selected record */ #define SC_RECORD_CURRENT 0UL /** * 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, 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 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 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); /** * Appends a record to the current (i.e. selected) file. * @param card struct sc_card object on which to issue the command * @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 written or an error value */ int sc_append_record(struct sc_card *card, const u8 * buf, size_t count, unsigned long flags); /** * 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 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 written or an error value */ 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 */ int sc_get_data(struct sc_card *, unsigned int, u8 *, size_t); 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. 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 */ int sc_get_challenge(struct sc_card *card, u8 * rndout, size_t len); /********************************************************************/ /* ISO 7816-8 related functions */ /********************************************************************/ int sc_restore_security_env(struct sc_card *card, int se_num); int sc_set_security_env(struct sc_card *card, const struct sc_security_env *env, int se_num); 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 *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 * command and in this case SC_ERROR_NOT_SUPPORTED is returned. * @param card struct sc_card object * @return SC_SUCCESS on success, SC_ERROR_NOT_SUPPORTED if the card * 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_change_reference_data(struct sc_card *card, unsigned int type, int ref, const u8 *old, size_t oldlen, const u8 *newref, size_t newlen, int *tries_left); int sc_reset_retry_counter(struct sc_card *card, unsigned int type, int ref, const u8 *puk, size_t puklen, 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 */ /********************************************************************/ int sc_create_file(struct sc_card *card, sc_file_t *file); int sc_delete_file(struct sc_card *card, const sc_path_t *path); /* Card controls */ int sc_card_ctl(struct sc_card *card, unsigned long command, void *arg); int sc_file_valid(const sc_file_t *file); sc_file_t * sc_file_new(void); void sc_file_free(sc_file_t *file); void sc_file_dup(sc_file_t **dest, const sc_file_t *src); int sc_file_add_acl_entry(sc_file_t *file, unsigned int operation, unsigned int method, unsigned long key_ref); const struct sc_acl_entry * sc_file_get_acl_entry(const sc_file_t *file, unsigned int operation); void sc_file_clear_acl_entries(sc_file_t *file, unsigned int operation); int sc_file_set_sec_attr(sc_file_t *file, const u8 *sec_attr, size_t sec_attr_len); int sc_file_set_prop_attr(sc_file_t *file, const u8 *prop_attr, size_t prop_attr_len); int sc_file_set_type_attr(sc_file_t *file, const u8 *type_attr, size_t type_attr_len); 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 */ /********************************************************************/ /** * Sets the content of a sc_path_t object. * @param path sc_path_t object to set * @param type type of path * @param id value of the path * @param id_len length of the path value * @param index index within the file * @param count number of bytes * @return SC_SUCCESS on success and an error code otherwise */ int sc_path_set(sc_path_t *path, int type, const u8 *id, size_t id_len, int index, int count); void sc_format_path(const char *path_in, sc_path_t *path_out); /** * Return string representation of the given sc_path_t object * Warning: as static memory is used for the return value * this function is not thread-safe !!! * @param path sc_path_t object of the path to be printed * @return pointer to a const buffer with the string representation * of the path */ const char *sc_print_path(const sc_path_t *path); /** * Prints the sc_path_t object to a character buffer * @param buf pointer to the buffer * @param buflen size of the buffer * @param path sc_path_t object to be printed * @return SC_SUCCESS on success and an error code otherwise */ int sc_path_print(char *buf, size_t buflen, const sc_path_t *path); /** * Compares two sc_path_t objects * @param patha sc_path_t object of the first path * @param pathb sc_path_t object of the second path * @return 1 if both paths are equal and 0 otherwise */ int sc_compare_path(const sc_path_t *patha, const sc_path_t *pathb); /** * Concatenate two sc_path_t values and store the result in * d (note: d can be the same as p1 or p2). * @param d destination sc_path_t object * @param p1 first sc_path_t object * @param p2 second sc_path_t object * @return SC_SUCCESS on success and an error code otherwise */ int sc_concatenate_path(sc_path_t *d, const sc_path_t *p1, const sc_path_t *p2); /** * Appends a sc_path_t object to another sc_path_t object (note: * this function is a wrapper for sc_concatenate_path) * @param dest destination sc_path_t object * @param src sc_path_t object to append * @return SC_SUCCESS on success and an error code otherwise */ int sc_append_path(sc_path_t *dest, const sc_path_t *src); /** * Checks whether one path is a prefix of another path * @param prefix sc_path_t object with the prefix * @param path sc_path_t object with the path which should start * with the given prefix * @return 1 if the parameter prefix is a prefix of path and 0 otherwise */ int sc_compare_path_prefix(const sc_path_t *prefix, const sc_path_t *path); int sc_append_path_id(sc_path_t *dest, const u8 *id, size_t idlen); int sc_append_file_id(sc_path_t *dest, unsigned int fid); /** * Returns a const sc_path_t object for the MF * @return sc_path_t object of the MF */ const sc_path_t *sc_get_mf_path(void); /********************************************************************/ /* miscellaneous functions */ /********************************************************************/ 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); /** * Initializes a given OID * @param oid sc_object_id object to be initialized */ void sc_init_oid(struct sc_object_id *oid); /** * Converts a given OID in ascii form to a internal sc_object_id object * @param oid OUT sc_object_id object for the result * @param in ascii string with the oid ("1.2.3.4.5...") * @return SC_SUCCESS or an error value if an error occurred. */ int sc_format_oid(struct sc_object_id *oid, const char *in); /** * Compares two sc_object_id objects * @param oid1 the first sc_object_id object * @param oid2 the second sc_object_id object * @return 1 if the oids are equal and a zero value otherwise */ int sc_compare_oid(const struct sc_object_id *oid1, const struct sc_object_id *oid2); /** * Validates a given OID * @param oid sc_object_id object to be validated */ int sc_valid_oid(const struct sc_object_id *oid); /* Base64 encoding/decoding functions */ int sc_base64_encode(const u8 *in, size_t inlen, u8 *out, size_t outlen, size_t linelength); int sc_base64_decode(const char *in, u8 *out, size_t outlen); /** * Clears a memory buffer (note: when OpenSSL is used this is * currently a wrapper for OPENSSL_cleanse() ). * @param ptr pointer to the memory buffer * @param len length of the memory buffer */ void sc_mem_clear(void *ptr, 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); int sc_make_cache_dir(sc_context_t *ctx); int sc_enum_apps(struct sc_card *card); 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_invalidate_cache(struct sc_card *card); void sc_print_cache(struct sc_card *card); struct sc_algorithm_info * sc_card_find_rsa_alg(struct sc_card *card, size_t key_length); struct sc_algorithm_info * sc_card_find_ec_alg(struct sc_card *card, 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, 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); /** * Get CRC-32 digest * @param value pointer to data used for CRC calculation * @param len length of data used for CRC calculation */ 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 -- * reset the header of the 'remote APDUs' list, set the handlers * to manipulate the list. */ void sc_remote_data_init(struct sc_remote_data *rdata); /** * Copy and allocate if needed EC parameters data * @dst destination * @src source */ int sc_copy_ec_params(struct sc_ec_parameters *, struct sc_ec_parameters *); struct sc_card_error { unsigned int SWs; int errorno; const char *errorstr; }; extern const char *sc_get_version(void); #define SC_IMPLEMENT_DRIVER_VERSION(a) \ static const char *drv_version = (a); \ const char *sc_driver_version()\ { \ return drv_version; \ } 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 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 * empty. * @param[in,out] ef_len Length of \a *ef * * @note The appropriate directory must be selected before calling this function. * */ int iso7816_read_binary_sfid(sc_card_t *card, unsigned char sfid, u8 **ef, size_t *ef_len); /** * @brief Write a complete 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_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); /** * 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 #endif OpenSC-0.26.1/src/libopensc/pace.h000066400000000000000000000051151474147347300165750ustar00rootroot00000000000000/* * opensc.h: PACE library header file * * Copyright (C) 2010-2012 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 _PACE_H #define _PACE_H #include #ifdef HAVE_UNISTD_H #include #endif #ifdef __cplusplus extern "C" { #endif #include "libopensc/opensc.h" #ifdef ENABLE_SM #include "libopensc/sm.h" #endif #define PACE_PIN_ID_MRZ 0x01 #define PACE_PIN_ID_CAN 0x02 #define PACE_PIN_ID_PIN 0x03 #define PACE_PIN_ID_PUK 0x04 /** * Input data for EstablishPACEChannel() */ struct establish_pace_channel_input { /** Type of secret (CAN, MRZ, PIN or PUK). */ unsigned char pin_id; /** Length of \a chat */ size_t chat_length; /** Card holder authorization template */ const unsigned char *chat; /** Length of \a pin */ size_t pin_length; /** Secret */ const unsigned char *pin; /** Length of \a certificate_description */ size_t certificate_description_length; /** Certificate description */ const unsigned char *certificate_description; }; /** * Output data for EstablishPACEChannel() */ struct establish_pace_channel_output { /** PACE result (TR-03119) */ unsigned int result; /** MSE: Set AT status byte */ unsigned char mse_set_at_sw1; /** MSE: Set AT status byte */ unsigned char mse_set_at_sw2; /** Length of \a ef_cardaccess */ size_t ef_cardaccess_length; /** EF.CardAccess */ unsigned char *ef_cardaccess; /** Length of \a recent_car */ size_t recent_car_length; /** Most recent certificate authority reference */ unsigned char *recent_car; /** Length of \a previous_car */ size_t previous_car_length; /** Previous certificate authority reference */ unsigned char *previous_car; /** Length of \a id_icc */ size_t id_icc_length; /** ICC identifier */ unsigned char *id_icc; /** Length of \a id_pcd */ size_t id_pcd_length; /** PCD identifier */ unsigned char *id_pcd; }; #ifdef __cplusplus } #endif #endif OpenSC-0.26.1/src/libopensc/padding.c000066400000000000000000000555401474147347300172750ustar00rootroot00000000000000/* * padding.c: miscellaneous padding functions * * Copyright (C) 2001, 2002 Juha Yrjölä * Copyright (C) 2003 - 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 * 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 #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 */ static const u8 hdr_md5[] = { 0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10 }; static const u8 hdr_sha1[] = { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 }; static const u8 hdr_sha256[] = { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 }; static const u8 hdr_sha384[] = { 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30 }; static const u8 hdr_sha512[] = { 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40 }; static const u8 hdr_sha224[] = { 0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04, 0x1c }; static const u8 hdr_ripemd160[] = { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x24, 0x03, 0x02, 0x01, 0x05, 0x00, 0x04, 0x14 }; static const struct digest_info_prefix { unsigned int algorithm; const u8 * hdr; size_t hdr_len; size_t hash_len; } digest_info_prefix[] = { { SC_ALGORITHM_RSA_HASH_NONE, NULL, 0, 0 }, { SC_ALGORITHM_RSA_HASH_MD5, hdr_md5, sizeof(hdr_md5), 16 }, { SC_ALGORITHM_RSA_HASH_SHA1, hdr_sha1, sizeof(hdr_sha1), 20 }, { SC_ALGORITHM_RSA_HASH_SHA256, hdr_sha256, sizeof(hdr_sha256), 32 }, { SC_ALGORITHM_RSA_HASH_SHA384, hdr_sha384, sizeof(hdr_sha384), 48 }, { SC_ALGORITHM_RSA_HASH_SHA512, hdr_sha512, sizeof(hdr_sha512), 64 }, { SC_ALGORITHM_RSA_HASH_SHA224, hdr_sha224, sizeof(hdr_sha224), 28 }, { SC_ALGORITHM_RSA_HASH_RIPEMD160,hdr_ripemd160, sizeof(hdr_ripemd160), 20 }, { SC_ALGORITHM_RSA_HASH_MD5_SHA1, NULL, 0, 36 }, { 0, NULL, 0, 0 } }; /* add/remove pkcs1 BT01 padding */ static int sc_pkcs1_add_01_padding(const u8 *in, size_t in_len, u8 *out, size_t *out_len, size_t mod_length) { size_t i; if (*out_len < mod_length) return SC_ERROR_BUFFER_TOO_SMALL; if (in_len + 11 > mod_length) return SC_ERROR_INVALID_ARGUMENTS; i = mod_length - in_len; memmove(out + i, in, in_len); *out++ = 0x00; *out++ = 0x01; memset(out, 0xFF, i - 3); out += i - 3; *out = 0x00; *out_len = mod_length; return SC_SUCCESS; } int sc_pkcs1_strip_01_padding(struct sc_context *ctx, const u8 *in_dat, size_t in_len, u8 *out, size_t *out_len) { const u8 *tmp = in_dat; size_t len; if (in_dat == NULL || in_len < 10) return SC_ERROR_INTERNAL; /* skip leading zero byte */ if (*tmp == 0) { tmp++; in_len--; } len = in_len; if (*tmp != 0x01) return SC_ERROR_WRONG_PADDING; for (tmp++, len--; *tmp == 0xff && len != 0; tmp++, len--) ; if (!len || (in_len - len) < 9 || *tmp++ != 0x00) return SC_ERROR_WRONG_PADDING; len--; if (out == NULL) /* just check the padding */ return SC_SUCCESS; if (*out_len < len) return SC_ERROR_INTERNAL; memmove(out, tmp, len); *out_len = len; return SC_SUCCESS; } /* 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_constant_time(sc_context_t *ctx, unsigned int n, const u8 *data, unsigned int data_len, u8 *out, unsigned int *out_len) { 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) LOG_FUNC_RETURN(ctx, SC_ERROR_INTERNAL); /* 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); } 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); } if (*data != 0) { sc_evp_md_free(mgf1_md); LOG_FUNC_RETURN(ctx, SC_ERROR_WRONG_PADDING); } dblen = len - 1 - mdlen; if (dblen > sizeof(db)) { sc_evp_md_free(mgf1_md); LOG_FUNC_RETURN(ctx, SC_ERROR_INTERNAL); } 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]; 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, const u8 *in, size_t in_len, u8 *out, size_t *out_len) { int i; for (i = 0; digest_info_prefix[i].algorithm != 0; i++) { if (algorithm == digest_info_prefix[i].algorithm) { const u8 *hdr = digest_info_prefix[i].hdr; size_t hdr_len = digest_info_prefix[i].hdr_len, hash_len = digest_info_prefix[i].hash_len; if (in_len != hash_len || *out_len < (hdr_len + hash_len)) return SC_ERROR_INTERNAL; memmove(out + hdr_len, in, hash_len); memmove(out, hdr, hdr_len); *out_len = hdr_len + hash_len; return SC_SUCCESS; } } return SC_ERROR_INTERNAL; } 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) { int i; for (i = 0; digest_info_prefix[i].algorithm != 0; i++) { 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) *algorithm = digest_info_prefix[i].algorithm; if (out_dat == NULL) /* just check the DigestInfo prefix */ return SC_SUCCESS; if (*out_len < hash_len) return SC_ERROR_INTERNAL; memmove(out_dat, in_dat + hdr_len, hash_len); *out_len = hash_len; return SC_SUCCESS; } } 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 = (mod_bits + 7) / 8; 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_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 = (mod_bits + 7) / 8; #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; 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 ((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); LOG_FUNC_RETURN(ctx, i); } tmp = out; } else { tmp_len = in_len; } switch(pad_algo) { case SC_ALGORITHM_RSA_PAD_NONE: /* padding done by card => nothing to do */ if (out != tmp) memcpy(out, tmp, tmp_len); *out_len = tmp_len; LOG_FUNC_RETURN(ctx, SC_SUCCESS); 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: /* 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); } } int sc_get_encoding_flags(sc_context_t *ctx, unsigned long iflags, unsigned long caps, unsigned long *pflags, unsigned long *sflags) { 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 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"); } sc_log(ctx, "pad flags 0x%lX, secure algorithm flags 0x%lX", *pflags, *sflags); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } OpenSC-0.26.1/src/libopensc/pkcs15-actalis.c000066400000000000000000000221731474147347300204070ustar00rootroot00000000000000/* * PKCS15 emulation layer for Actalis card. * To see how this works, run p15dump on your Actalis Card. * * Copyright (C) 2005, Andrea Frigido * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #ifdef ENABLE_ZLIB #include #endif #include "common/compat_strlcpy.h" #include "libopensc/pkcs15.h" #include "libopensc/log.h" #include "libopensc/internal.h" 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) { int r; 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); else return r; } 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); } #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); } #endif static int sc_pkcs15emu_actalis_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_buf[13], *serial; int flags; 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 = SC_PKCS15_OPTS_CACHE_ALL_FILES; /* Get Serial number */ sc_format_path("3F0030000001", &path); r = sc_select_file(card, &path, NULL); if (r != SC_SUCCESS) return SC_ERROR_WRONG_CARD; r = sc_read_binary(card, 0xC3, serial_buf, 12, 0); if (r != SC_SUCCESS) return SC_ERROR_WRONG_CARD; serial = serial_buf; /* * The serial number is 8 characters long. Later versions of the * card have the serial number at a different offset, after 4 more * bytes. */ if (serial[0] != 'H') { if (serial[4] == 'H') serial = &serial_buf[4]; else return SC_ERROR_WRONG_CARD; } serial[8] = '\0'; /* Controllo che il serial number inizi per "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); #ifdef ENABLE_ZLIB for (i = 0; i < 3; i++) { sc_path_t cpath; sc_format_path(certPath[i], &cpath); if (sc_select_file(card, &cpath, NULL) == SC_SUCCESS) { unsigned char *compCert = NULL, *cert = NULL, size[2]; unsigned long compLen, len; sc_pkcs15_cert_info_t cert_info; sc_pkcs15_object_t cert_obj; memset(&cert_info, 0, sizeof(cert_info)); memset(&cert_obj, 0, sizeof(cert_obj)); 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 */ cert = malloc(len * sizeof(unsigned char)); 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; } 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 = (int)len; sc_pkcs15_cache_file(p15card, &cpath, cert, len); id.value[0] = j + 1; id.len = 1; cert_info.id = id; cert_info.path = cpath; cert_info.authority = (j>0); strlcpy(cert_obj.label, certLabel[j], sizeof(cert_obj.label)); j++; cert_obj.flags = SC_PKCS15_CO_FLAG_MODIFIABLE; 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_format_path(pinDfName, &path); path.type = SC_PATH_TYPE_DF_NAME; id.value[0] = 1; id.len = 1; sc_pkcs15emu_add_pin(p15card, &id, authPIN, &path, 0x81, SC_PKCS15_PIN_TYPE_ASCII_NUMERIC, 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; 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, 0x08, &auth_id, SC_PKCS15_CO_FLAG_PRIVATE); /* return to MF */ sc_format_path("3F00", &path); sc_select_file(card, &path, NULL); { /* 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 actalis_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_actalis_init_ex(sc_pkcs15_card_t * p15card, struct sc_aid *aid) { if (actalis_detect_card(p15card)) return SC_ERROR_WRONG_CARD; return sc_pkcs15emu_actalis_init(p15card); } OpenSC-0.26.1/src/libopensc/pkcs15-algo.c000066400000000000000000000437411474147347300177150ustar00rootroot00000000000000/* * pkc15-algo.c: ASN.1 handling for algorithm IDs and parameters * * Copyright (C) 2001, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include "internal.h" #include "asn1.h" /* * AlgorithmIdentifier handling */ static struct sc_asn1_entry c_asn1_des_iv[] = { { "iv", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_OCTET_STRING, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; static int asn1_decode_des_params(sc_context_t *ctx, void **paramp, const u8 *buf, size_t buflen, int depth) { struct sc_asn1_entry asn1_des_iv[2]; u8 iv[8]; 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); r = _sc_asn1_decode(ctx, asn1_des_iv, buf, buflen, NULL, NULL, 0, depth + 1); if (r < 0) return r; if (ivlen != 8) return SC_ERROR_INVALID_ASN1_OBJECT; *paramp = malloc(8); if (!*paramp) return SC_ERROR_OUT_OF_MEMORY; memcpy(*paramp, iv, 8); return 0; } static int asn1_encode_des_params(sc_context_t *ctx, void *params, u8 **buf, size_t *buflen, int depth) { struct sc_asn1_entry asn1_des_iv[2]; int ivlen = 8; sc_copy_asn1_entry(c_asn1_des_iv, asn1_des_iv); sc_format_asn1_entry(asn1_des_iv + 0, params, &ivlen, 1); return _sc_asn1_encode(ctx, asn1_des_iv, buf, buflen, depth + 1); } static const struct sc_asn1_entry c_asn1_gostr3410_params0[] = { { "GOSTR3410Params", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; static const struct sc_asn1_entry c_asn1_gostr3410_params[] = { { "key_params", SC_ASN1_OBJECT, SC_ASN1_TAG_OBJECT, 0, NULL, NULL }, { "hash_params", SC_ASN1_OBJECT, SC_ASN1_TAG_OBJECT, 0, NULL, NULL }, { "cipher_params", SC_ASN1_OBJECT, SC_ASN1_TAG_OBJECT, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; static int asn1_decode_gostr3410_params(sc_context_t *ctx, void **paramp, const u8 *buf, size_t buflen, int depth) { struct sc_asn1_entry asn1_gostr3410_params0[2], asn1_gostr3410_params[4]; struct sc_object_id keyp, hashp, cipherp; int r; sc_copy_asn1_entry(c_asn1_gostr3410_params0, asn1_gostr3410_params0); sc_copy_asn1_entry(c_asn1_gostr3410_params, asn1_gostr3410_params); sc_format_asn1_entry(asn1_gostr3410_params0 + 0, asn1_gostr3410_params, NULL, 0); sc_format_asn1_entry(asn1_gostr3410_params + 0, &keyp, NULL, 0); sc_format_asn1_entry(asn1_gostr3410_params + 1, &hashp, NULL, 0); sc_format_asn1_entry(asn1_gostr3410_params + 2, &cipherp, NULL, 0); r = _sc_asn1_decode(ctx, asn1_gostr3410_params0, buf, buflen, NULL, NULL, 0, depth + 1); /* TODO: store in paramp */ (void)paramp; /* no warning */ return r; } static int asn1_encode_gostr3410_params(sc_context_t *ctx, void *params, u8 **buf, size_t *buflen, int depth) { struct sc_asn1_entry asn1_gostr3410_params0[2], asn1_gostr3410_params[4]; struct sc_pkcs15_gost_parameters *gost_params = (struct sc_pkcs15_gost_parameters *)params; int r; sc_copy_asn1_entry(c_asn1_gostr3410_params0, asn1_gostr3410_params0); sc_copy_asn1_entry(c_asn1_gostr3410_params, asn1_gostr3410_params); sc_format_asn1_entry(asn1_gostr3410_params0 + 0, asn1_gostr3410_params, NULL, 1); sc_format_asn1_entry(asn1_gostr3410_params + 0, &gost_params->key, NULL, 1); sc_format_asn1_entry(asn1_gostr3410_params + 1, &gost_params->hash, NULL, 1); /* sc_format_asn1_entry(asn1_gostr3410_params + 2, &cipherp, NULL, 1); */ r = _sc_asn1_encode(ctx, asn1_gostr3410_params0, buf, buflen, depth + 1); sc_log(ctx, "encoded-params: %s", sc_dump_hex(*buf, *buflen)); return r; } static const struct sc_asn1_entry c_asn1_pbkdf2_params[] = { { "salt", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_OCTET_STRING, 0, NULL, NULL }, { "count", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, { "keyLength", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, SC_ASN1_OPTIONAL, NULL, NULL }, { "prf", SC_ASN1_ALGORITHM_ID, SC_ASN1_TAG_SEQUENCE, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; static int asn1_decode_pbkdf2_params(sc_context_t *ctx, void **paramp, const u8 *buf, size_t buflen, int depth) { struct sc_pbkdf2_params info; struct sc_asn1_entry asn1_pbkdf2_params[5]; int r; sc_copy_asn1_entry(c_asn1_pbkdf2_params, asn1_pbkdf2_params); sc_format_asn1_entry(asn1_pbkdf2_params + 0, info.salt, &info.salt_len, 0); sc_format_asn1_entry(asn1_pbkdf2_params + 1, &info.iterations, NULL, 0); sc_format_asn1_entry(asn1_pbkdf2_params + 2, &info.key_length, NULL, 0); sc_format_asn1_entry(asn1_pbkdf2_params + 3, &info.hash_alg, NULL, 0); memset(&info, 0, sizeof(info)); info.salt_len = sizeof(info.salt); info.hash_alg.algorithm = SC_ALGORITHM_SHA1; r = _sc_asn1_decode(ctx, asn1_pbkdf2_params, buf, buflen, NULL, NULL, 0, depth + 1); if (r < 0) return r; *paramp = malloc(sizeof(info)); if (!*paramp) return SC_ERROR_OUT_OF_MEMORY; memcpy(*paramp, &info, sizeof(info)); return 0; } static int asn1_encode_pbkdf2_params(sc_context_t *ctx, void *params, u8 **buf, size_t *buflen, int depth) { struct sc_pbkdf2_params *info; struct sc_asn1_entry asn1_pbkdf2_params[5]; info = (struct sc_pbkdf2_params *) params; sc_copy_asn1_entry(c_asn1_pbkdf2_params, asn1_pbkdf2_params); sc_format_asn1_entry(asn1_pbkdf2_params + 0, info->salt, &info->salt_len, 1); sc_format_asn1_entry(asn1_pbkdf2_params + 1, &info->iterations, NULL, 1); if (info->key_length > 0) sc_format_asn1_entry(asn1_pbkdf2_params + 2, &info->key_length, NULL, 1); if (info->hash_alg.algorithm != SC_ALGORITHM_SHA1) sc_format_asn1_entry(asn1_pbkdf2_params + 3, &info->hash_alg, NULL, 0); return _sc_asn1_encode(ctx, asn1_pbkdf2_params, buf, buflen, depth + 1); } static const struct sc_asn1_entry c_asn1_pbes2_params[] = { { "keyDerivationAlg", SC_ASN1_ALGORITHM_ID, SC_ASN1_TAG_SEQUENCE, 0, NULL, NULL }, { "keyEcnryptionAlg", SC_ASN1_ALGORITHM_ID, SC_ASN1_TAG_SEQUENCE, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; static int asn1_decode_pbes2_params(sc_context_t *ctx, void **paramp, const u8 *buf, size_t buflen, int depth) { struct sc_asn1_entry asn1_pbes2_params[3]; struct sc_pbes2_params info; int r; sc_copy_asn1_entry(c_asn1_pbes2_params, asn1_pbes2_params); sc_format_asn1_entry(asn1_pbes2_params + 0, &info.derivation_alg, NULL, 0); sc_format_asn1_entry(asn1_pbes2_params + 1, &info.key_encr_alg, NULL, 0); memset(&info, 0, sizeof(info)); r = _sc_asn1_decode(ctx, asn1_pbes2_params, buf, buflen, NULL, NULL, 0, depth + 1); if (r < 0) return r; *paramp = malloc(sizeof(info)); if (!*paramp) return SC_ERROR_OUT_OF_MEMORY; memcpy(*paramp, &info, sizeof(info)); return 0; } static int asn1_encode_pbes2_params(sc_context_t *ctx, void *params, u8 **buf, size_t *buflen, int depth) { struct sc_asn1_entry asn1_pbes2_params[3]; struct sc_pbes2_params *info; info = (struct sc_pbes2_params *) params; sc_copy_asn1_entry(c_asn1_pbes2_params, asn1_pbes2_params); sc_format_asn1_entry(asn1_pbes2_params + 0, &info->derivation_alg, NULL, 0); sc_format_asn1_entry(asn1_pbes2_params + 1, &info->key_encr_alg, NULL, 0); return _sc_asn1_encode(ctx, asn1_pbes2_params, buf, buflen, depth + 1); } static void asn1_free_pbes2_params(void *ptr) { struct sc_pbes2_params *params = (struct sc_pbes2_params *) ptr; sc_asn1_clear_algorithm_id(¶ms->derivation_alg); sc_asn1_clear_algorithm_id(¶ms->key_encr_alg); free(params); } static const struct sc_asn1_entry c_asn1_ec_params[] = { { "ecParameters", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, { "namedCurve", SC_ASN1_OBJECT, SC_ASN1_TAG_OBJECT, 0, NULL, NULL}, { "implicityCA", SC_ASN1_NULL, SC_ASN1_TAG_NULL, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; static int asn1_decode_ec_params(sc_context_t *ctx, void **paramp, const u8 *buf, size_t buflen, int depth) { int r; struct sc_object_id curve; struct sc_asn1_entry asn1_ec_params[4]; struct sc_ec_parameters *ecp; memset(&curve, 0, sizeof(curve)); /* We only want to copy the parms if they are a namedCurve * or ecParameters nullParam aka implicityCA is not to be * used with PKCS#11 2.20 */ sc_copy_asn1_entry(c_asn1_ec_params, asn1_ec_params); sc_format_asn1_entry(asn1_ec_params + 1, &curve, 0, 0); /* Some signature algorithms will not have any data */ if (buflen == 0 || buf == NULL) return 0; r = sc_asn1_decode_choice(ctx, asn1_ec_params, buf, buflen, NULL, NULL); /* r = index in asn1_ec_params */ sc_debug(ctx, SC_LOG_DEBUG_ASN1, "asn1_decode_ec_params r=%d", r); if (r < 0) return r; ecp = calloc(1, sizeof(struct sc_ec_parameters)); if (ecp == NULL) return SC_ERROR_OUT_OF_MEMORY; if (r <= 1) { ecp->der.value = malloc(buflen); if (ecp->der.value == NULL) { free(ecp); return SC_ERROR_OUT_OF_MEMORY; } ecp->der.len = buflen; memcpy(ecp->der.value, buf, buflen); } else { r = 0; } ecp->type = r; /* but 0 = ecparams if any, 1=named curve */ *paramp = ecp; return SC_SUCCESS; }; static int asn1_encode_ec_params(sc_context_t *ctx, void *params, u8 **buf, size_t *buflen, int depth) { struct sc_ec_parameters *ecp = (struct sc_ec_parameters *) params; /* Only handle named curves. They may be absent too */ sc_debug(ctx, SC_LOG_DEBUG_ASN1, "asn1_encode_ec_params() called"); *buf = NULL; *buflen = 0; if (ecp && ecp->type == 1 && ecp->der.value) { /* named curve */ *buf = malloc(ecp->der.len); if (*buf == NULL) return SC_ERROR_OUT_OF_MEMORY; memcpy(*buf, ecp->der.value, ecp->der.len); *buflen = ecp->der.len; } else { sc_debug(ctx, SC_LOG_DEBUG_ASN1, "Not named curve"); } return 0; } static void asn1_free_ec_params(void *params) { struct sc_ec_parameters *ecp = (struct sc_ec_parameters *) params; if (ecp) { if (ecp->der.value) free(ecp->der.value); if (ecp->named_curve) free(ecp->named_curve); free(ecp); } } 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 }, #endif #ifdef SC_ALGORITHM_MD5 { SC_ALGORITHM_MD5, {{ 1, 2, 840, 113549, 2, 5, -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 }, #endif #ifdef SC_ALGORITHM_DH { 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 }, #endif #ifdef SC_ALGORITHM_RC2 /* CBC mode */ { SC_ALGORITHM_RC2, {{ 1, 2, 840, 113549, 3, 2, -1}}, asn1_decode_rc2_params, asn1_encode_rc2_params }, #endif #ifdef SC_ALGORITHM_DES /* CBC mode */ { SC_ALGORITHM_DES, {{ 1, 3, 14, 3, 2, 7, -1}}, asn1_decode_des_params, asn1_encode_des_params, 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 }, #endif #ifdef SC_ALGORITHM_3DES /* EDE CBC mode */ { SC_ALGORITHM_3DES, {{ 1, 2, 840, 113549, 3, 7, -1}}, asn1_decode_des_params, asn1_encode_des_params, free }, #endif #ifdef SC_ALGORITHM_GOST /* EDE CBC mode */ { 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}}, asn1_decode_gostr3410_params, asn1_encode_gostr3410_params, 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}}, asn1_decode_pbkdf2_params, asn1_encode_pbkdf2_params, free }, #endif #ifdef SC_ALGORITHM_PBES2 { SC_ALGORITHM_PBES2, {{ 1, 2, 840, 113549, 1, 5, 13, -1}}, asn1_decode_pbes2_params, asn1_encode_pbes2_params, asn1_free_pbes2_params }, #endif #ifdef SC_ALGORITHM_EC { SC_ALGORITHM_EC, {{ 1, 2, 840, 10045, 2, 1, -1}}, asn1_decode_ec_params, asn1_encode_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}, #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}}, asn1_decode_ec_params, asn1_encode_ec_params, asn1_free_ec_params }, #endif #ifdef SC_ALGORITHM_ECDSA_SHA256 { SC_ALGORITHM_ECDSA_SHA256, {{ 1, 2, 840, 10045, 4, 3, 2, -1}}, asn1_decode_ec_params, asn1_encode_ec_params, asn1_free_ec_params }, #endif #ifdef SC_ALGORITHM_ECDSA_SHA384 { SC_ALGORITHM_ECDSA_SHA384, {{ 1, 2, 840, 10045, 4, 3, 3, -1}}, asn1_decode_ec_params, asn1_encode_ec_params, asn1_free_ec_params }, #endif #ifdef SC_ALGORITHM_ECDSA_SHA512 { 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}}, NULL, NULL, NULL }, #endif #ifdef SC_ALGORITHM_XEDDSA /* aka curve25519 */ /* RFC 8410, needed to parse/create X509 certs/pubkeys */ { SC_ALGORITHM_XEDDSA, {{1, 3, 101, 110, -1}}, NULL, NULL, NULL }, #endif { -1, {{ -1 }}, NULL, NULL, NULL } }; static struct sc_asn1_pkcs15_algorithm_info * sc_asn1_get_algorithm_info(const struct sc_algorithm_id *id) { struct sc_asn1_pkcs15_algorithm_info *aip = NULL; for (aip = algorithm_table; aip->id >= 0; aip++) { if ((int) id->algorithm < 0 && sc_compare_oid(&id->oid, &aip->oid)) return aip; if (aip->id == (int)id->algorithm) return aip; } return NULL; } static const struct sc_asn1_entry c_asn1_alg_id[3] = { { "algorithm", SC_ASN1_OBJECT, SC_ASN1_TAG_OBJECT, 0, NULL, NULL }, { "nullParam", SC_ASN1_NULL, SC_ASN1_TAG_NULL, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; int sc_asn1_decode_algorithm_id(struct sc_context *ctx, const unsigned char *in, size_t len, struct sc_algorithm_id *id, int depth) { struct sc_asn1_pkcs15_algorithm_info *alg_info = NULL; struct sc_asn1_entry asn1_alg_id[3]; int r; LOG_FUNC_CALLED(ctx); sc_copy_asn1_entry(c_asn1_alg_id, asn1_alg_id); sc_format_asn1_entry(asn1_alg_id + 0, &id->oid, NULL, 0); memset(id, 0, sizeof(*id)); r = _sc_asn1_decode(ctx, asn1_alg_id, in, len, &in, &len, 0, depth + 1); LOG_TEST_RET(ctx, r, "ASN.1 parsing of algo ID failed"); sc_log(ctx, "decoded OID '%s'", sc_dump_oid(&(id->oid))); /* See if we understand the algorithm, and if we do, check * whether we know how to decode any additional parameters */ id->algorithm = (unsigned int ) -1; alg_info = sc_asn1_get_algorithm_info(id); if (alg_info != NULL) { id->algorithm = alg_info->id; if (alg_info->decode) { if (asn1_alg_id[1].flags & SC_ASN1_PRESENT) { sc_log(ctx, "SC_ASN1_PRESENT was set, so invalid"); LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ASN1_OBJECT); } r = alg_info->decode(ctx, &id->params, in, len, depth); } } LOG_FUNC_RETURN(ctx, r); } int sc_asn1_encode_algorithm_id(struct sc_context *ctx, u8 **buf, size_t *len, const struct sc_algorithm_id *id, int depth) { struct sc_asn1_pkcs15_algorithm_info *alg_info; struct sc_algorithm_id temp_id; struct sc_asn1_entry asn1_alg_id[3]; u8 *obj = NULL; size_t obj_len = 0; int r; u8 *tmp; LOG_FUNC_CALLED(ctx); 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 %lu", id->algorithm); LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); } /* Set the oid if not yet given */ if (!sc_valid_oid(&id->oid)) { temp_id = *id; temp_id.oid = alg_info->oid; id = &temp_id; } sc_log(ctx, "encode algo %s", sc_dump_oid(&(id->oid))); sc_copy_asn1_entry(c_asn1_alg_id, asn1_alg_id); sc_format_asn1_entry(asn1_alg_id + 0, (void *) &id->oid, NULL, 1); /* no parameters, write NULL tag */ /* If it's EDDSA/XEDDSA, according to RFC8410, params * MUST be absent */ 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); LOG_TEST_RET(ctx, r, "ASN.1 encode of algorithm failed"); /* Encode any parameters */ if (id->params && alg_info->encode) { r = alg_info->encode(ctx, id->params, &obj, &obj_len, depth+1); if (r < 0) { if (obj) free(obj); LOG_FUNC_RETURN(ctx, r); } } if (obj_len) { tmp = (u8 *) realloc(*buf, *len + obj_len); if (!tmp) { free(*buf); *buf = NULL; free(obj); LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); } *buf = tmp; memcpy(*buf + *len, obj, obj_len); *len += obj_len; free(obj); } sc_log(ctx, "return encoded algorithm ID: %s", sc_dump_hex(*buf, *len)); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } void sc_asn1_clear_algorithm_id(struct sc_algorithm_id *id) { struct sc_asn1_pkcs15_algorithm_info *aip; if (id->params && (aip = sc_asn1_get_algorithm_info(id)) && aip->free) { aip->free(id->params); id->params = NULL; } } OpenSC-0.26.1/src/libopensc/pkcs15-atrust-acos.c000066400000000000000000000165121474147347300212340ustar00rootroot00000000000000/* * partial PKCS15 emulation for A-Trust ACOS cards * * Copyright (C) 2005 Franz Brandl based on work from * Nils Larsch , TrustCenter AG * * 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 "common/compat_strlcpy.h" #include "libopensc/pkcs15.h" #include "libopensc/cardctl.h" #define MANU_ID "A-Trust" #define CARD_LABEL "a.sign Premium a" 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; static int get_cert_len(sc_card_t *card, sc_path_t *path) { int r; u8 buf[8]; r = sc_select_file(card, path, NULL); if (r < 0) return 0; r = sc_read_binary(card, 0, buf, sizeof(buf), 0); if (r < 0) return 0; if (buf[0] != 0x30 || buf[1] != 0x82) return 0; path->index = 0; path->count = ((((int) buf[2]) << 8) | buf[3]) + 4; return 1; } static int acos_detect_card(sc_pkcs15_card_t *p15card) { int r; u8 buf[128]; sc_path_t path; sc_card_t *card = p15card->card; /* check if we have the correct card OS */ if (strncmp(card->name, "A-TRUST ACOS", strlen("A-TRUST ACOS"))) return SC_ERROR_WRONG_CARD; /* read EF_CIN_CSN file */ sc_format_path("DF71D001", &path); r = sc_select_file(card, &path, NULL); if (r != SC_SUCCESS) return SC_ERROR_WRONG_CARD; r = sc_read_binary(card, 0, buf, 8, 0); if (r != 8) return SC_ERROR_WRONG_CARD; return SC_SUCCESS; } static int sc_pkcs15emu_atrust_acos_init(sc_pkcs15_card_t *p15card) { const cdata certs[] = { {"C.CH.EKEY", 0, "DF71C001","1", 0},/* Decryption Certificate */ {NULL, 0, NULL, NULL, 0} }; const pindata pins[] = { { "01", "PIN.DEC", "3F00DF71", 0x81, /* Decryption PIN */ SC_PKCS15_PIN_TYPE_ASCII_NUMERIC, 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} }; const prdata prkeys[] = { { "01", "SK.CH.EKEY", 1536, SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP, "", /* do not specify file here to prevent reset of security state */ 0x88, "01", SC_PKCS15_CO_FLAG_PRIVATE}, { NULL, NULL, 0, 0, NULL, 0, NULL, 0} }; int r, i; u8 buf[256]; char buf2[256]; sc_path_t path; sc_file_t *file = NULL; sc_card_t *card = p15card->card; /* get serial number */ /* read EF_CIN_CSN file */ sc_format_path("DF71D001", &path); r = sc_select_file(card, &path, NULL); if (r != SC_SUCCESS) return SC_ERROR_INTERNAL; r = sc_read_binary(card, 0, buf, 8, 0); if (r != 8) return SC_ERROR_INTERNAL; r = sc_bin_to_hex(buf, 8, buf2, sizeof(buf2), 0); if (r != SC_SUCCESS) return SC_ERROR_INTERNAL; set_string(&p15card->tokeninfo->serial_number, buf2); if (!p15card->tokeninfo->serial_number) return SC_ERROR_INTERNAL; /* manufacturer ID */ set_string(&p15card->tokeninfo->manufacturer_id, MANU_ID); if (!p15card->tokeninfo->manufacturer_id) goto err; /* card label */ set_string(&p15card->tokeninfo->label, CARD_LABEL); if (!p15card->tokeninfo->label) goto err; /* set certs */ 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); if (!get_cert_len(card, &cert_info.path)) /* skip errors */ continue; strlcpy(cert_obj.label, certs[i].label, sizeof(cert_obj.label)); cert_obj.flags = certs[i].obj_flags; r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info); if (r < 0) goto err; } /* set pins */ 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.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) goto err; } /* 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); 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) goto err; } /* select the application DF */ sc_format_path("DF71", &path); r = sc_select_file(card, &path, &file); if (r != SC_SUCCESS || !file) goto err; /* set the application DF */ 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) { if (acos_detect_card(p15card)) return SC_ERROR_WRONG_CARD; return sc_pkcs15emu_atrust_acos_init(p15card); } OpenSC-0.26.1/src/libopensc/pkcs15-cac.c000066400000000000000000000312071474147347300175130ustar00rootroot00000000000000/* * partial PKCS15 emulation for CAC-II cards * only minimal use of the authentication cert and key * * Copyright (C) 2005,2006,2007,2008,2009,2010 * Douglas E. Engert * 2004, Nils Larsch * Copyright (C) 2006, Identity Alliance, * Thomas Harning * Copyright (C) 2007, EMC, Russell Larner * Copyright (C) 2016, Red Hat, Inc. * * CAC driver author: Robert Relyea * * 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" #include "cardctl.h" #include "pkcs15.h" /* X509 Key Usage flags */ #include "../pkcs15init/pkcs15-init.h" /* probably should get manufacturer ID from cuid */ #define MANU_ID "Common Access Card" 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 unsigned char pad_char; int obj_flags; } pindata; static int cac_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_CAC_GENERIC || card->type >= SC_CARD_TYPE_CAC_GENERIC+1000) return SC_ERROR_INVALID_CARD; return SC_SUCCESS; } #define CAC_NUM_CERTS_AND_KEYS 10 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"); } static int sc_pkcs15emu_cac_init(sc_pkcs15_card_t *p15card) { static const pindata pins[] = { { "1", "PIN", "", 0x00, SC_PKCS15_PIN_TYPE_ASCII_NUMERIC, 8, 4, 8, SC_PKCS15_PIN_FLAG_NEEDS_PADDING | SC_PKCS15_PIN_FLAG_INITIALIZED , -1, 0xFF, SC_PKCS15_CO_FLAG_PRIVATE }, { NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; /* oid for key usage */ static const struct sc_object_id usage_type = {{ 2, 5, 29, 15, -1 }}; unsigned int usage; /* * 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. * Key usages bits taken from certificate key usage extension. */ 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; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); memset(&serial, 0, sizeof(serial)); /* could read this off card if needed */ 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_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 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; const char * label; 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; 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); 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); 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); 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 * 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, "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; struct sc_pkcs15_pubkey_info pubkey_info; struct sc_pkcs15_prkey_info prkey_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; 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)); memset(&prkey_info, 0, sizeof(prkey_info)); memset(&cert_obj, 0, sizeof(cert_obj)); memset(&pubkey_obj, 0, sizeof(pubkey_obj)); memset(&prkey_obj, 0, sizeof(prkey_obj)); cert_info.id = obj_info.id; pubkey_info.id = obj_info.id; prkey_info.id = obj_info.id; cert_info.path = obj_info.path; prkey_info.path = obj_info.path; /* Add 0x3f00 to the front of prkey_info.path to make sc_key_file happy */ /* only do this if our path.len is 1 or 2 */ if (prkey_info.path.len && prkey_info.path.len <= 2) { prkey_info.path.value[2] = prkey_info.path.value[0]; prkey_info.path.value[3] = prkey_info.path.value[1]; prkey_info.path.value[0] = 0x3f; prkey_info.path.value[1] = 0x00; 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]; prkey_info.native = 1; memcpy(cert_obj.label, obj_info.app_label, sizeof(obj_info.app_label)); memcpy(pubkey_obj.label, obj_info.app_label, sizeof(obj_info.app_label)); memcpy(prkey_obj.label, obj_info.app_label, sizeof(obj_info.app_label)); 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, 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_info.value.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_info.value.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) { 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_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); 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) { (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."); 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_card_t *card = p15card->card; sc_context_t *ctx = card->ctx; LOG_FUNC_CALLED(ctx); if (cac_detect_card(p15card)) return SC_ERROR_WRONG_CARD; return sc_pkcs15emu_cac_init(p15card); } OpenSC-0.26.1/src/libopensc/pkcs15-cache.c000066400000000000000000000140251474147347300200270ustar00rootroot00000000000000/* * pkcs15-cache.c: PKCS #15 file caching functions * * Copyright (C) 2001, 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., 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 #include #include #include #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, const sc_path_t *path, char *buf, size_t bufsize) { char dir[PATH_MAX]; 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); r = sc_get_cache_dir(p15card->card->ctx, dir, sizeof(dir)); if (r) return r; snprintf(dir + strlen(dir), sizeof(dir) - strlen(dir), "/"); last_update = sc_pkcs15_get_lastupdate(p15card); if (!last_update) last_update = "NODATE"; if (p15card->tokeninfo->serial_number) { snprintf(dir + strlen(dir), sizeof(dir) - strlen(dir), "%s_%s", p15card->tokeninfo->serial_number, last_update); } else { snprintf(dir + strlen(dir), sizeof(dir) - strlen(dir), "uid-%s_%s", sc_dump_hex( p15card->card->uid.value, 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), "_"); for (u = 0; u < path->aid.len; u++) snprintf(dir + strlen(dir), sizeof(dir) - strlen(dir), "%02X", path->aid.value[u]); } else if (path->type != SC_PATH_TYPE_PATH) { return SC_ERROR_INVALID_ARGUMENTS; } if (path->len) { size_t offs = 0; if (path->len > 2 && memcmp(path->value, "\x3F\x00", 2) == 0) offs = 2; snprintf(dir + strlen(dir), sizeof(dir) - strlen(dir), "_"); for (u = 0; u < path->len - offs; u++) snprintf(dir + strlen(dir), sizeof(dir) - strlen(dir), "%02X", path->value[u + offs]); } if (!buf) return SC_ERROR_BUFFER_TOO_SMALL; strlcpy(buf, dir, bufsize); return SC_SUCCESS; } int sc_pkcs15_read_cached_file(struct sc_pkcs15_card *p15card, const sc_path_t *path, u8 **buf, size_t *bufsize) { char fname[PATH_MAX]; int rv; FILE *f; size_t count; struct stat stbuf; u8 *data = NULL; if (path->len < 2) return SC_ERROR_INVALID_ARGUMENTS; /* Accept full path or FILE-ID path with AID */ if ((path->type != SC_PATH_TYPE_PATH) && (path->type != SC_PATH_TYPE_FILE_ID || path->aid.len == 0)) return SC_ERROR_INVALID_ARGUMENTS; sc_log(p15card->card->ctx, "try to read cache for %s", sc_print_path(path)); rv = generate_cache_filename(p15card, path, fname, sizeof(fname)); if (rv != SC_SUCCESS) return rv; sc_log(p15card->card->ctx, "read cached file %s", fname); f = fopen(fname, "rb"); if (!f) return SC_ERROR_FILE_NOT_FOUND; if (fstat(fileno(f), &stbuf)) { fclose(f); return SC_ERROR_FILE_NOT_FOUND; } if (path->count < 0) { count = stbuf.st_size; } else { count = path->count; if (path->index + count > (size_t)stbuf.st_size) { rv = SC_ERROR_FILE_NOT_FOUND; /* cache file bad? */ goto err; } if (0 != fseek(f, (long)path->index, SEEK_SET)) { rv = SC_ERROR_FILE_NOT_FOUND; goto err; } } if (*buf == NULL) { data = malloc((size_t)stbuf.st_size); if (data == NULL) { rv = SC_ERROR_OUT_OF_MEMORY; goto err; } } else { if (count > *bufsize) { rv = SC_ERROR_BUFFER_TOO_SMALL; goto err; } data = *buf; } if (count != fread(data, 1, count, f)) { rv = SC_ERROR_BUFFER_TOO_SMALL; goto err; } *buf = data; *bufsize = count; rv = SC_SUCCESS; err: if (rv != SC_SUCCESS) { if (data != *buf) { free(data); } } fclose(f); return rv; } int sc_pkcs15_cache_file(struct sc_pkcs15_card *p15card, const sc_path_t *path, const u8 *buf, size_t bufsize) { char fname[PATH_MAX]; int r; long len; FILE *f; size_t c; r = generate_cache_filename(p15card, path, fname, sizeof(fname)); if (r != 0) return r; 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, "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_log(p15card->card->ctx, "fwrite() wrote only %"SC_FORMAT_LEN_SIZE_T"u bytes", c); unlink(fname); return SC_ERROR_INTERNAL; } return 0; } OpenSC-0.26.1/src/libopensc/pkcs15-cardos.c000066400000000000000000000130451474147347300202400ustar00rootroot00000000000000/* * 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); } OpenSC-0.26.1/src/libopensc/pkcs15-cert.c000066400000000000000000000565561474147347300177400ustar00rootroot00000000000000/* * pkcs15-cert.c: PKCS #15 certificate functions * * Copyright (C) 2001, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include #include "internal.h" #include "asn1.h" #include "pkcs15.h" 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 = {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; struct sc_asn1_entry asn1_version[] = { { "version", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, &cert->version, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; struct sc_asn1_entry asn1_extensions[] = { { "x509v3", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL| SC_ASN1_ALLOC, &cert->extensions, &cert->extensions_len }, { NULL, 0, 0, 0, NULL, NULL } }; struct sc_asn1_entry asn1_tbscert[] = { { "version", SC_ASN1_STRUCT, SC_ASN1_CTX | 0 | SC_ASN1_CONS, SC_ASN1_OPTIONAL, asn1_version, NULL }, { "serialNumber", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_INTEGER, SC_ASN1_ALLOC, &serial, &serial_len }, { "signature", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, { "issuer", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_ALLOC, &issuer, &issuer_len }, { "validity", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, { "subject", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_ALLOC, &subject, &subject_len }, /* Use a callback to get the algorithm, parameters and pubkey into sc_pkcs15_pubkey */ { "subjectPublicKeyInfo",SC_ASN1_CALLBACK, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, sc_pkcs15_pubkey_from_spki_fields, &pubkey }, { "extensions", SC_ASN1_STRUCT, SC_ASN1_CTX | 3 | SC_ASN1_CONS, SC_ASN1_OPTIONAL, asn1_extensions, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; struct sc_asn1_entry asn1_cert[] = { { "tbsCertificate", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, asn1_tbscert, NULL }, { "signatureAlgorithm", SC_ASN1_ALGORITHM_ID, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, &sig_alg, NULL }, { "signatureValue", SC_ASN1_BIT_STRING, SC_ASN1_TAG_BIT_STRING, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; struct sc_asn1_entry asn1_serial_number[] = { { "serialNumber", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_INTEGER, SC_ASN1_ALLOC, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; struct sc_asn1_entry asn1_subject[] = { { "subject", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_ALLOC, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; struct sc_asn1_entry asn1_issuer[] = { { "issuer", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_ALLOC, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; 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) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ASN1_OBJECT, "X.509 certificate not found"); data_len = objlen + (obj - buf); cert->data.value = malloc(data_len); if (!cert->data.value) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); memcpy(cert->data.value, buf, data_len); cert->data.len = data_len; r = sc_asn1_decode(ctx, asn1_cert, obj, objlen, NULL, NULL); cert->key = pubkey; cert->version++; LOG_TEST_GOTO_ERR(ctx, r, "ASN.1 parsing of certificate failed"); if (!pubkey) LOG_TEST_GOTO_ERR(ctx, SC_ERROR_INVALID_ASN1_OBJECT, "Unable to decode subjectPublicKeyInfo from cert"); 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); 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); 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); LOG_TEST_GOTO_ERR(ctx, r, "ASN.1 encoding of issuer"); } err: /* not used for anything */ sc_asn1_clear_algorithm_id(&sig_alg); free(serial); free(subject); free(issuer); LOG_FUNC_RETURN(ctx, r); } /* Get a component of Distinguished Name (e.i. subject or issuer) USING the oid tag. * dn can be either cert->subject or cert->issuer. * dn_len would be cert->subject_len or cert->issuer_len. * * Common types: * CN: struct sc_object_id type = {{2, 5, 4, 3, -1}}; * Country: struct sc_object_id type = {{2, 5, 4, 6, -1}}; * L: struct sc_object_id type = {{2, 5, 4, 7, -1}}; * S: struct sc_object_id type = {{2, 5, 4, 8, -1}}; * O: struct sc_object_id type = {{2, 5, 4, 10, -1}}; * OU: struct sc_object_id type = {{2, 5, 4, 11, -1}}; * * if *name is NULL, sc_pkcs15_get_name_from_dn will allocate space for name. */ 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) { const u8 *rdn = NULL; const u8 *next_ava = NULL; size_t rdn_len = 0; size_t next_ava_len = 0; int rv; 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 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 = 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); if (ava == NULL) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ASN1_OBJECT, "ASN.1 decoding of AVA"); /* It would be nice to use sc_asn1_decode here to parse the entire AVA, but we are missing 1 critical * function in the templates: the ability to accept any tag for value. This prevents us from just * grabbing the value as is out of the template. AVA's can have tags of PRINTABLE_STRING, * TELETEXSTRING, T61STRING or UTF8_STRING with PRINTABLE_STRING and UTF8_STRING being the most common. * The other feature that would be nice is returning a pointer to our requested data using the space * of the parent (basically what this code is doing here), rather than allocating and copying. */ /* unwrap the sequence */ dummy = ava; dummy_len = ava_len; ava = sc_asn1_skip_tag(ctx, &dummy, &dummy_len, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, &ava_len); if (ava == NULL) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ASN1_OBJECT, "ASN.1 decoding of AVA"); /* unwrap the oid */ oidp = sc_asn1_skip_tag(ctx, &ava, &ava_len, SC_ASN1_TAG_OBJECT, &oid_len); if (ava == NULL) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ASN1_OBJECT, "ASN.1 decoding of AVA OID"); /* Convert to OID */ rv = sc_asn1_decode_object_id(oidp, oid_len, &oid); if (rv != SC_SUCCESS) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ASN1_OBJECT, "ASN.1 decoding of AVA OID"); if (sc_compare_oid(&oid, type) == 0) continue; /* 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) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); *name_len = dummy_len; } *name_len = MIN(dummy_len, *name_len); memcpy(*name, dummy, *name_len); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } LOG_FUNC_RETURN(ctx, SC_ERROR_ASN1_OBJECT_NOT_FOUND); } /* Get a specific extension from the cert. * 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 extension is critical * and 0 if it is not. * The data in the extension is extension specific. * The following are common extension values: * Subject Key ID: struct sc_object_id type = {{2, 5, 29, 14, -1}}; * Key Usage: struct sc_object_id type = {{2, 5, 29, 15, -1}}; * Subject Alt Name: struct sc_object_id type = {{2, 5, 29, 17, -1}}; * Basic Constraints: struct sc_object_id type = {{2, 5, 29, 19, -1}}; * CRL Distribution Points: struct sc_object_id type = {{2, 5, 29, 31, -1}}; * Certificate Policies: struct sc_object_id type = {{2, 5, 29, 32, -1}}; * Extended Key Usage: struct sc_object_id type = {{2, 5, 29, 37, -1}}; * * if *ext_val is NULL, sc_pkcs15_get_extension will allocate space for ext_val. */ int sc_pkcs15_get_extension(struct sc_context *ctx, struct sc_pkcs15_cert *cert, const struct sc_object_id *type, u8 **ext_val, size_t *ext_val_len, int *is_critical) { const u8 *ext = NULL; const u8 *next_ext = NULL; size_t ext_len = 0; size_t next_ext_len = 0; struct sc_object_id oid; u8 *val = NULL; size_t val_len = 0; int critical; int r; struct sc_asn1_entry asn1_cert_ext[] = { { "x509v3 entry OID", SC_ASN1_OBJECT, SC_ASN1_TAG_OBJECT, 0, &oid, 0 }, { "criticalFlag", SC_ASN1_BOOLEAN, SC_ASN1_TAG_BOOLEAN, SC_ASN1_OPTIONAL, &critical, NULL }, { "extensionValue",SC_ASN1_OCTET_STRING, SC_ASN1_TAG_OCTET_STRING, SC_ASN1_ALLOC, &val, &val_len }, { 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, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, &ext_len); if (ext == NULL) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ASN1_OBJECT, "ASN.1 decoding of AVA"); /* * use the sc_asn1_decoder for clarity. NOTE it would be more efficient to do this by hand * so we avoid the many malloc/frees here, but one hopes that one day the asn1_decode will allow * a 'static pointer' flag that returns a const pointer to the actual asn1 space so we only need * to make a final copy of the extension value before we return */ critical = 0; r = sc_asn1_decode(ctx, asn1_cert_ext, ext, ext_len, NULL, NULL); if (r < 0) LOG_FUNC_RETURN(ctx, r); /* is it the RN we are looking for */ if (sc_compare_oid(&oid, type) != 0) { if (*ext_val == NULL) { *ext_val = val; val = NULL; *ext_val_len = val_len; /* do not free here -- return the allocated value to caller */ } else { *ext_val_len = MIN(*ext_val_len, val_len); if (val) { memcpy(*ext_val, val, *ext_val_len); free(val); } } if (is_critical) *is_critical = critical; r = (int)val_len; LOG_FUNC_RETURN(ctx, r); } if (val) { free(val); val = NULL; } } if (val) free(val); LOG_FUNC_RETURN(ctx, SC_ERROR_ASN1_OBJECT_NOT_FOUND); } /* * Get an extension whose value is a bit string. These include keyUsage and extendedKeyUsage. * See above for the other parameters. */ int sc_pkcs15_get_bitstring_extension(struct sc_context *ctx, struct sc_pkcs15_cert *cert, const struct sc_object_id *type, 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_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); free(bit_string); LOG_TEST_RET(ctx, r, "Decoding extension bit string"); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } int sc_pkcs15_pubkey_from_cert(struct sc_context *ctx, struct sc_pkcs15_der *cert_blob, struct sc_pkcs15_pubkey **out) { int rv; struct sc_pkcs15_cert * cert; cert = calloc(1, sizeof(struct sc_pkcs15_cert)); if (cert == NULL) return SC_ERROR_OUT_OF_MEMORY; rv = parse_x509_cert(ctx, cert_blob, cert); *out = cert->key; cert->key = NULL; sc_pkcs15_free_certificate(cert); LOG_FUNC_RETURN(ctx, rv); } int sc_pkcs15_read_certificate(struct sc_pkcs15_card *p15card, const struct sc_pkcs15_cert_info *info, int private_obj, struct sc_pkcs15_cert **cert_out) { struct sc_context *ctx = NULL; struct sc_pkcs15_cert *cert = NULL; struct sc_pkcs15_der der; int r; if (p15card == NULL || info == NULL || cert_out == NULL) { return SC_ERROR_INVALID_ARGUMENTS; } ctx = p15card->card->ctx; LOG_FUNC_CALLED(ctx); if (info->value.len && info->value.value) { sc_der_copy(&der, &info->value); } else if (info->path.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 { LOG_FUNC_RETURN(ctx, SC_ERROR_OBJECT_NOT_FOUND); } cert = malloc(sizeof(struct sc_pkcs15_cert)); if (cert == NULL) { free(der.value); LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); } memset(cert, 0, sizeof(struct sc_pkcs15_cert)); if (parse_x509_cert(ctx, &der, cert)) { free(der.value); sc_pkcs15_free_certificate(cert); LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ASN1_OBJECT); } free(der.value); *cert_out = cert; LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static const struct sc_asn1_entry c_asn1_cred_ident[] = { { "idType", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, { "idValue", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_OCTET_STRING, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; static const struct sc_asn1_entry c_asn1_com_cert_attr[] = { { "iD", SC_ASN1_PKCS15_ID, SC_ASN1_TAG_OCTET_STRING, 0, NULL, NULL }, { "authority", SC_ASN1_BOOLEAN, SC_ASN1_TAG_BOOLEAN, SC_ASN1_OPTIONAL, NULL, NULL }, { "identifier", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, /* FIXME: Add rest of the optional fields */ { NULL, 0, 0, 0, NULL, NULL } }; static const struct sc_asn1_entry c_asn1_x509_cert_value_choice[] = { { "path", SC_ASN1_PATH, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { "direct", SC_ASN1_OCTET_STRING, SC_ASN1_CTX | 0 | SC_ASN1_CONS, SC_ASN1_OPTIONAL | SC_ASN1_ALLOC, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; static const struct sc_asn1_entry c_asn1_x509_cert_attr[] = { { "value", SC_ASN1_CHOICE, 0, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; static const struct sc_asn1_entry c_asn1_type_cert_attr[] = { { "x509CertificateAttributes", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; static const struct sc_asn1_entry c_asn1_cert[] = { { "x509Certificate", SC_ASN1_PKCS15_OBJECT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; int sc_pkcs15_decode_cdf_entry(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *obj, const u8 ** buf, size_t *buflen) { sc_context_t *ctx = p15card->card->ctx; struct sc_pkcs15_cert_info info; struct sc_asn1_entry asn1_cred_ident[3], asn1_com_cert_attr[4], asn1_x509_cert_attr[2], asn1_type_cert_attr[2], asn1_cert[2], asn1_x509_cert_value_choice[3]; struct sc_asn1_pkcs15_object cert_obj = { obj, asn1_com_cert_attr, NULL, asn1_type_cert_attr }; sc_pkcs15_der_t *der = &info.value; u8 id_value[128]; int id_type; size_t id_value_len = sizeof(id_value); int r; sc_copy_asn1_entry(c_asn1_cred_ident, asn1_cred_ident); sc_copy_asn1_entry(c_asn1_com_cert_attr, asn1_com_cert_attr); sc_copy_asn1_entry(c_asn1_x509_cert_attr, asn1_x509_cert_attr); sc_copy_asn1_entry(c_asn1_x509_cert_value_choice, asn1_x509_cert_value_choice); sc_copy_asn1_entry(c_asn1_type_cert_attr, asn1_type_cert_attr); sc_copy_asn1_entry(c_asn1_cert, asn1_cert); sc_format_asn1_entry(asn1_cred_ident + 0, &id_type, NULL, 0); sc_format_asn1_entry(asn1_cred_ident + 1, &id_value, &id_value_len, 0); sc_format_asn1_entry(asn1_com_cert_attr + 0, &info.id, NULL, 0); sc_format_asn1_entry(asn1_com_cert_attr + 1, &info.authority, NULL, 0); sc_format_asn1_entry(asn1_com_cert_attr + 2, asn1_cred_ident, NULL, 0); sc_format_asn1_entry(asn1_x509_cert_attr + 0, asn1_x509_cert_value_choice, NULL, 0); sc_format_asn1_entry(asn1_x509_cert_value_choice + 0, &info.path, NULL, 0); sc_format_asn1_entry(asn1_x509_cert_value_choice + 1, &der->value, &der->len, 0); sc_format_asn1_entry(asn1_type_cert_attr + 0, asn1_x509_cert_attr, NULL, 0); sc_format_asn1_entry(asn1_cert + 0, &cert_obj, NULL, 0); /* Fill in defaults */ memset(&info, 0, sizeof(info)); info.authority = 0; r = sc_asn1_decode(ctx, asn1_cert, *buf, *buflen, buf, buflen); /* In case of error, trash the cert value (direct coding) */ if (r < 0 && der->value) free(der->value); if (r == SC_ERROR_ASN1_END_OF_CONTENTS) return r; LOG_TEST_RET(ctx, r, "ASN.1 decoding failed"); 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"); } else { info.path.aid = p15card->app->ddo.aid; } 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) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); memcpy(obj->data, &info, sizeof(info)); return 0; } int sc_pkcs15_encode_cdf_entry(sc_context_t *ctx, const struct sc_pkcs15_object *obj, u8 **buf, size_t *bufsize) { struct sc_asn1_entry asn1_cred_ident[3], asn1_com_cert_attr[4], asn1_x509_cert_attr[2], asn1_type_cert_attr[2], asn1_cert[2], asn1_x509_cert_value_choice[3]; struct sc_pkcs15_cert_info *infop = (sc_pkcs15_cert_info_t *) obj->data; sc_pkcs15_der_t *der = &infop->value; struct sc_asn1_pkcs15_object cert_obj = { (struct sc_pkcs15_object *) obj, asn1_com_cert_attr, NULL, asn1_type_cert_attr }; int r; sc_copy_asn1_entry(c_asn1_cred_ident, asn1_cred_ident); sc_copy_asn1_entry(c_asn1_com_cert_attr, asn1_com_cert_attr); sc_copy_asn1_entry(c_asn1_x509_cert_attr, asn1_x509_cert_attr); sc_copy_asn1_entry(c_asn1_x509_cert_value_choice, asn1_x509_cert_value_choice); sc_copy_asn1_entry(c_asn1_type_cert_attr, asn1_type_cert_attr); sc_copy_asn1_entry(c_asn1_cert, asn1_cert); sc_format_asn1_entry(asn1_com_cert_attr + 0, (void *) &infop->id, NULL, 1); if (infop->authority) sc_format_asn1_entry(asn1_com_cert_attr + 1, (void *) &infop->authority, NULL, 1); if (infop->path.len || !der->value) { sc_format_asn1_entry(asn1_x509_cert_value_choice + 0, &infop->path, NULL, 1); } else { sc_format_asn1_entry(asn1_x509_cert_value_choice + 1, der->value, &der->len, 1); } sc_format_asn1_entry(asn1_type_cert_attr + 0, &asn1_x509_cert_value_choice, NULL, 1); sc_format_asn1_entry(asn1_cert + 0, (void *) &cert_obj, NULL, 1); r = sc_asn1_encode(ctx, asn1_cert, buf, bufsize); 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_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) { if (cert == NULL) { return; } sc_pkcs15_free_pubkey(cert->key); free(cert->subject); free(cert->issuer); free(cert->serial); free(cert->data.value); free(cert->extensions); free(cert); } void sc_pkcs15_free_cert_info(sc_pkcs15_cert_info_t *cert) { if (!cert) return; free(cert->value.value); free(cert); } OpenSC-0.26.1/src/libopensc/pkcs15-coolkey.c000066400000000000000000000537531474147347300204440ustar00rootroot00000000000000/* * partial PKCS15 emulation for Coolkey cards * * Copyright (C) 2005,2006,2007,2008,2009,2010 * Douglas E. Engert * 2004, Nils Larsch * Copyright (C) 2006, Identity Alliance, * Thomas Harning * Copyright (C) 2007, EMC, Russell Larner * Copyright (C) 2016, Red Hat, Inc. * * Coolkey driver author: Robert Relyea * * 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" #include "cardctl.h" #include "asn1.h" #include "pkcs15.h" #include "../pkcs11/pkcs11.h" 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 unsigned char pad_char; int obj_flags; } pindata; static int coolkey_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_COOLKEY_GENERIC || card->type >= SC_CARD_TYPE_COOLKEY_GENERIC+1000) return SC_ERROR_INVALID_CARD; return SC_SUCCESS; } static int coolkey_get_object(sc_card_t *card, unsigned long object_id, sc_cardctl_coolkey_object_t **obj) { sc_cardctl_coolkey_find_object_t fobj; int r; fobj.type = SC_CARDCTL_COOLKEY_FIND_BY_ID; fobj.find_id = object_id; fobj.obj = NULL; r = sc_card_ctl(card, SC_CARDCTL_COOLKEY_FIND_OBJECT, &fobj); if (r < 0) { return r; } *obj = fobj.obj; return SC_SUCCESS; } /* * fetch attributes from an object */ static int coolkey_get_attribute(sc_card_t *card, sc_cardctl_coolkey_object_t *obj, CK_ATTRIBUTE_TYPE type, const u8 **val, size_t *val_len, u8 *data_type) { sc_cardctl_coolkey_attribute_t attribute; int r; attribute.object = obj; attribute.attribute_type = type; r = sc_card_ctl(card, SC_CARDCTL_COOLKEY_GET_ATTRIBUTE, &attribute); if (r < 0) { return r; } *val = attribute.attribute_value; *val_len = attribute.attribute_length; if (data_type) { *data_type = attribute.attribute_data_type; } return SC_SUCCESS; } static int coolkey_find_matching_cert(sc_card_t *card, sc_cardctl_coolkey_object_t *in_obj, sc_cardctl_coolkey_object_t **cert_obj) { sc_cardctl_coolkey_find_object_t fobj; sc_cardctl_coolkey_attribute_t template[2]; u8 obj_class[4]; int r; /* we are searching for certs .. */ template[0].attribute_type = CKA_CLASS; template[0].attribute_data_type = SC_CARDCTL_COOLKEY_ATTR_TYPE_ULONG; template[0].attribute_length = sizeof(obj_class); template[0].attribute_value = obj_class; ulong2bebytes(obj_class, CKO_CERTIFICATE); /* fetch the current object's CKA_ID */ template[1].attribute_type = CKA_ID; template[1].object = in_obj; r = sc_card_ctl(card, SC_CARDCTL_COOLKEY_GET_ATTRIBUTE, &template[1]); if (r < 0) { return r; } template[0].object = NULL; /*paranoia */ template[1].object = NULL; /*paranoia */ /* now find the cert that has the ID */ fobj.type = SC_CARDCTL_COOLKEY_FIND_BY_TEMPLATE; fobj.obj = NULL; fobj.coolkey_template = &template[0]; fobj.template_count=2; r = sc_card_ctl(card, SC_CARDCTL_COOLKEY_FIND_OBJECT, &fobj); if (r < 0) { return r; } *cert_obj = fobj.obj; return SC_SUCCESS; } 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 = NULL; size_t val_len = 0; u8 data_type = 0; int r; r = coolkey_get_attribute(card, obj, type, &val, &val_len, &data_type); if (r < 0) { return r; } if ((data_type != SC_CARDCTL_COOLKEY_ATTR_TYPE_ULONG) && (val_len != sizeof(CK_ULONG))) { return SC_ERROR_CORRUPTED_DATA; } *value = bebytes2ulong(val); return SC_SUCCESS; } 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 = NULL; size_t val_len = 0; r = coolkey_get_attribute(card, obj, attr_type, &val, &val_len, NULL); if (r < 0) { /* attribute not valid for this object, set boolean to false */ return 0; } if ((val_len == 1) && (*val == 1)) { return 1; } return 0; } 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 = 0; int r; r = coolkey_get_attribute(card, obj, type, &val, &val_len, NULL); if (r < 0) { return r; } if (val_len > max_data_len) { val_len = max_data_len; } memcpy(data, val, val_len); *data_len = val_len; return SC_SUCCESS; } static int coolkey_get_attribute_bytes_alloc(sc_card_t *card, sc_cardctl_coolkey_object_t *obj, CK_ATTRIBUTE_TYPE type, u8 **data, size_t *data_len) { const u8 *val; size_t val_len; int r; r = coolkey_get_attribute(card, obj, type, &val, &val_len, NULL); if (r < 0) { return r; } *data = malloc(val_len); if (*data == NULL) { return SC_ERROR_OUT_OF_MEMORY; } memcpy(*data, val, val_len); *data_len = val_len; return SC_SUCCESS; } static int coolkey_get_id(sc_card_t *card, sc_cardctl_coolkey_object_t *obj, struct sc_pkcs15_id *id) { return coolkey_get_attribute_bytes(card, obj, CKA_ID, id->value , &id->len, sizeof(id->value)); } /* * A number of opensc structure have the same layout, use a common function to fill them * int: * structure name first parameter second parameter * sc_lv_data unsigned char * value size_t len * sc_pkcs15_data u8 *data size_t data_len * sc_pkcs15_bignum u8 *data size_t len * sc_pkcs15_der u8 *value size_t len * sc_pkcs15_u8 u8 *value size_t len * * The following can properly assign all of them */ int coolkey_get_attribute_lv(sc_card_t *card, sc_cardctl_coolkey_object_t *obj, CK_ATTRIBUTE_TYPE type, void *ptr) { struct sc_pkcs15_data *item = (struct sc_pkcs15_data *)ptr; return coolkey_get_attribute_bytes_alloc(card, obj, type, &item->data, &item->data_len); } #define COOLKEY_ID_CERT ((unsigned long)'c') #define COOLKEY_ID_KEY ((unsigned long)'k') #define COOLKEY_ID_CERT_DATA ((unsigned long)'C') static unsigned long coolkey_get_object_type(unsigned long object_id) { return ((object_id >> 24 ) & 0xff); } static unsigned long coolkey_make_new_id(unsigned long object_id, unsigned long id_type) { return ((object_id & 0x00ffffffUL)|(id_type << 24)); } /* * We need cert data to fill in some of our keys. Also, some older tokens store the cert data in a separate * object from the rest of the cert attributes. This function handles both of these complications */ static int coolkey_get_certificate(sc_card_t *card, sc_cardctl_coolkey_object_t *obj, struct sc_pkcs15_der *cert) { sc_cardctl_coolkey_object_t *cert_obj; unsigned long object_id; int r; cert_obj = obj; if (coolkey_get_object_type(obj->id) != COOLKEY_ID_CERT) { r = coolkey_find_matching_cert(card, obj, &cert_obj); if (r < 0) { return r; } } r = coolkey_get_attribute_lv(card, cert_obj, CKA_VALUE, cert); if (r == SC_ERROR_DATA_OBJECT_NOT_FOUND) { object_id = coolkey_make_new_id(cert_obj->id, COOLKEY_ID_CERT_DATA); r = coolkey_get_object(card, object_id, &cert_obj); if (r < 0) { return r; } /* fill in cert data */ cert->value = malloc(cert_obj->length); if (cert->value == NULL) { return SC_ERROR_OUT_OF_MEMORY; } memcpy(cert->value, cert_obj->data, cert_obj->length); cert->len = cert_obj->length; return SC_SUCCESS; } return r; } struct coolkey_attr_flags { CK_ATTRIBUTE_TYPE attribute_type; unsigned int pkcs15_flags; }; static struct coolkey_attr_flags usage_table[] = { { CKA_ENCRYPT, SC_PKCS15_PRKEY_USAGE_ENCRYPT }, { CKA_DECRYPT, SC_PKCS15_PRKEY_USAGE_DECRYPT }, { CKA_SIGN, SC_PKCS15_PRKEY_USAGE_SIGN }, { CKA_SIGN_RECOVER, SC_PKCS15_PRKEY_USAGE_SIGNRECOVER }, { CKA_WRAP, SC_PKCS15_PRKEY_USAGE_WRAP }, { CKA_UNWRAP, SC_PKCS15_PRKEY_USAGE_UNWRAP }, { CKA_VERIFY, SC_PKCS15_PRKEY_USAGE_VERIFY }, { CKA_VERIFY_RECOVER, SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER }, { CKA_DERIVE, SC_PKCS15_PRKEY_USAGE_DERIVE } }; static struct coolkey_attr_flags access_table[] = { { CKA_SENSITIVE, SC_PKCS15_PRKEY_ACCESS_SENSITIVE }, { CKA_EXTRACTABLE, SC_PKCS15_PRKEY_ACCESS_EXTRACTABLE }, { CKA_ALWAYS_SENSITIVE, SC_PKCS15_PRKEY_ACCESS_ALWAYSSENSITIVE }, { CKA_NEVER_EXTRACTABLE,SC_PKCS15_PRKEY_ACCESS_NEVEREXTRACTABLE}, { CKA_LOCAL, SC_PKCS15_PRKEY_ACCESS_LOCAL } }; static struct coolkey_attr_flags flag_table[] = { { CKA_PRIVATE, SC_PKCS15_CO_FLAG_PRIVATE }, { CKA_MODIFIABLE, SC_PKCS15_CO_FLAG_MODIFIABLE } }; static int usage_table_size = sizeof(usage_table)/sizeof(usage_table[0]); static int access_table_size = sizeof(access_table)/sizeof(access_table[0]); static int flag_table_size = sizeof(flag_table)/sizeof(flag_table[0]); static int coolkey_set_bool_flags(sc_card_t *card, sc_cardctl_coolkey_object_t *obj, unsigned int *flags_ptr, struct coolkey_attr_flags *table, int table_size) { unsigned int flags = 0; int i; for (i=0; i< table_size; i++) { if (coolkey_get_attribute_boolean(card, obj, table[i].attribute_type)) { flags |= table[i].pkcs15_flags; } } *flags_ptr = flags; return SC_SUCCESS; } /* map a cert usage and algorithm to public and private key usages */ static int coolkey_get_usage(sc_card_t *card, sc_cardctl_coolkey_object_t *obj, unsigned int *usage_ptr) { return coolkey_set_bool_flags(card, obj, usage_ptr, usage_table, usage_table_size); } /* map a cert usage and algorithm to public and private key usages */ static int coolkey_get_flags(sc_card_t *card, sc_cardctl_coolkey_object_t *obj, unsigned int *flag_ptr) { return coolkey_set_bool_flags(card, obj, flag_ptr, flag_table, flag_table_size); } static int coolkey_get_access(sc_card_t *card, sc_cardctl_coolkey_object_t *obj, unsigned int *access_ptr) { return coolkey_set_bool_flags(card, obj, access_ptr, access_table, access_table_size); } /* * turn a coolkey object into a pkcss 15 pubkey. object should already be type * CKO_PUBLIC_KEY */ static sc_pkcs15_pubkey_t * coolkey_make_public_key(sc_card_t *card, sc_cardctl_coolkey_object_t *obj, CK_KEY_TYPE key_type) { sc_pkcs15_pubkey_t *key; int r; key = calloc(1, sizeof(struct sc_pkcs15_pubkey)); if (!key) return NULL; switch (key_type) { case CKK_RSA: key->algorithm = SC_ALGORITHM_RSA; r = coolkey_get_attribute_lv(card, obj, CKA_MODULUS, &key->u.rsa.modulus); if (r != SC_SUCCESS) { goto fail; } r = coolkey_get_attribute_lv(card, obj, CKA_PUBLIC_EXPONENT, &key->u.rsa.exponent); if (r != SC_SUCCESS) { goto fail; } break; case CKK_EC: key->algorithm = SC_ALGORITHM_EC; r = coolkey_get_attribute_bytes_alloc(card, obj, CKA_EC_POINT, &key->u.ec.ecpointQ.value, &key->u.ec.ecpointQ.len); if(r < 0) { goto fail; } r = coolkey_get_attribute_bytes_alloc(card, obj, CKA_EC_PARAMS, &key->u.ec.params.der.value, &key->u.ec.params.der.len); if (r < 0) { goto fail; } r = sc_pkcs15_fix_ec_parameters(card->ctx, &key->u.ec.params); if (r < 0) { goto fail; } break; } return key; fail: sc_pkcs15_free_pubkey(key); /* now parse the DER cert */ return NULL; } static sc_pkcs15_pubkey_t * coolkey_get_public_key_from_certificate(sc_pkcs15_card_t *p15card, sc_cardctl_coolkey_object_t *obj) { sc_pkcs15_cert_info_t cert_info; sc_pkcs15_cert_t *cert_out = NULL; sc_pkcs15_pubkey_t *key = NULL; int r, private_obj; unsigned int flags; memset(&cert_info, 0, sizeof(cert_info)); r = coolkey_get_certificate(p15card->card, obj, &cert_info.value); if (r < 0) { goto fail; } 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; } key = cert_out->key; cert_out->key = NULL; /* adopt the key from the cert */ fail: if (cert_out) { sc_pkcs15_free_certificate(cert_out); } if (cert_info.value.value) { free(cert_info.value.value); } return key; } static sc_pkcs15_pubkey_t * coolkey_get_public_key(sc_pkcs15_card_t *p15card, sc_cardctl_coolkey_object_t *obj, CK_KEY_TYPE key_type) { sc_pkcs15_pubkey_t *key; key = coolkey_make_public_key(p15card->card, obj, key_type); if (key) { return key; } return coolkey_get_public_key_from_certificate(p15card, obj); } 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, 32, 4, 32, SC_PKCS15_PIN_FLAG_INITIALIZED, -1, 0xFF, SC_PKCS15_CO_FLAG_PRIVATE }, { NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; /* * 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. * Key usages bits taken from certificate key usage extension. */ int r, i; 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); memset(&serial, 0, sizeof(serial)); /* coolkey caches a nonce once it logs in, don't keep the pin around. The card will * stay logged in until it's been pulled from the reader, in which case you want to reauthenticate * 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 */ set_string(&p15card->tokeninfo->label, "Coolkey"); set_string(&p15card->tokeninfo->manufacturer_id, "Unknown"); set_string(&p15card->tokeninfo->serial_number, "00000000"); } /* set 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; const char * label; 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; label = pins[i].label? pins[i].label : p15card->tokeninfo->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); 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_pubkey_info pubkey_info; struct sc_pkcs15_prkey_info prkey_info; sc_pkcs15_pubkey_t *key = NULL; void *obj_info = NULL; int obj_type = 0; CK_KEY_TYPE key_type; CK_OBJECT_CLASS obj_class; size_t len; r = (card->ops->card_ctl)(card, SC_CARDCTL_COOLKEY_GET_NEXT_OBJECT, &coolkey_obj); 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) { sc_pkcs15_format_id(pins[0].id, &obj_obj.auth_id); } r = coolkey_get_attribute_ulong(card, &coolkey_obj, CKA_CLASS, &obj_class); if (r < 0) { goto fail; } 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) { key_type = CKK_RSA; r = SC_SUCCESS; } if (r < 0) { goto fail; } /* set the info values */ obj_info = &prkey_info; 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 = (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); key = coolkey_get_public_key(p15card, &coolkey_obj, key_type); if (key_type == CKK_RSA) { obj_type = SC_PKCS15_TYPE_PRKEY_RSA; if (key) { prkey_info.modulus_length = key->u.rsa.modulus.len*8; } } else if (key_type == CKK_EC) { obj_type = SC_PKCS15_TYPE_PRKEY_EC; if (key) { prkey_info.field_length = key->u.ec.params.field_length; } } else { goto fail; } 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) { key_type = CKK_RSA; r = SC_SUCCESS; } if (r < 0) { goto fail; } key = coolkey_get_public_key(p15card, &coolkey_obj, key_type); if (key == NULL) { goto fail; } /* set the info values */ 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); 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 = (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; } else if (key_type == CKK_EC) { obj_type = SC_PKCS15_TYPE_PUBKEY_EC; pubkey_info.field_length = key->u.ec.params.field_length; } else { free(pubkey_info.direct.spki.value); goto fail; } /* set the obj values */ obj_obj.emulated = key; key = NULL; 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); cert_info.path = coolkey_obj.path; obj_type = SC_PKCS15_TYPE_CERT_X509; /* following will find the cached cert in cert_info */ r = coolkey_get_certificate(card, &coolkey_obj, &cert_info.value); if (r < 0) { goto fail; } break; default: /* no other recognized types which are stored 'on card' */ sc_log(card->ctx, "Unknown object type %lu, skipping", obj_class); continue; } r = sc_pkcs15emu_object_add(p15card, obj_type, &obj_obj, obj_info); if (r != SC_SUCCESS) sc_log(card->ctx, "sc_pkcs15emu_object_add() returned %d", r); fail: if (key) { sc_pkcs15_free_pubkey(key); } } 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_card_t *card = p15card->card; sc_context_t *ctx = card->ctx; int rv; LOG_FUNC_CALLED(ctx); 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); } OpenSC-0.26.1/src/libopensc/pkcs15-data.c000066400000000000000000000135551474147347300177040ustar00rootroot00000000000000/* * pkcs15-data.c: PKCS #15 data object functions * * Copyright (C) 2002 Danny De Cock * * This source file was inspired by pkcs15-cert.c. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include "internal.h" #include "asn1.h" #include "pkcs15.h" #include "common/compat_strnlen.h" 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_pkcs15_data *data_object; struct sc_pkcs15_der der; int r; LOG_FUNC_CALLED(ctx); if (!info || !data_object_out) 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, private_obj); LOG_TEST_RET(ctx, r, "Cannot get DATA object data"); } r = sc_der_copy(&der, &info->data); LOG_TEST_RET(ctx, r, "Cannot allocate memory for der value"); 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"); } data_object->data = der.value; data_object->data_len = der.len; *data_object_out = data_object; LOG_FUNC_RETURN(ctx,SC_SUCCESS); } static const struct sc_asn1_entry c_asn1_data[] = { { "data", SC_ASN1_PKCS15_OBJECT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; static const struct sc_asn1_entry c_asn1_com_data_attr[] = { { "appName", SC_ASN1_UTF8STRING, SC_ASN1_TAG_UTF8STRING, SC_ASN1_OPTIONAL, NULL, NULL }, { "appOID", SC_ASN1_OBJECT, SC_ASN1_TAG_OBJECT, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; static const struct sc_asn1_entry c_asn1_type_data_attr[] = { { "path", SC_ASN1_PATH, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; int sc_pkcs15_decode_dodf_entry(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *obj, const u8 ** buf, size_t *buflen) { sc_context_t *ctx = p15card->card->ctx; struct sc_pkcs15_data_info info; struct sc_asn1_entry asn1_com_data_attr[3], asn1_type_data_attr[2], 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) - 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); sc_format_asn1_entry(asn1_com_data_attr + 0, &info.app_label, &label_len, 0); sc_format_asn1_entry(asn1_com_data_attr + 1, &info.app_oid, NULL, 0); sc_format_asn1_entry(asn1_type_data_attr + 0, &info.path, NULL, 0); sc_format_asn1_entry(asn1_data + 0, &data_obj, NULL, 0); /* Fill in defaults */ memset(&info, 0, sizeof(info)); sc_init_oid(&info.app_oid); r = sc_asn1_decode(ctx, asn1_data, *buf, *buflen, buf, buflen); if (r == SC_ERROR_ASN1_END_OF_CONTENTS) return r; LOG_TEST_RET(ctx, r, "ASN.1 decoding failed"); 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; } else { info.path.aid = p15card->app->ddo.aid; } obj->type = SC_PKCS15_TYPE_DATA_OBJECT; obj->data = malloc(sizeof(info)); if (obj->data == NULL) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); memcpy(obj->data, &info, sizeof(info)); return SC_SUCCESS; } int sc_pkcs15_encode_dodf_entry(sc_context_t *ctx, const struct sc_pkcs15_object *obj, u8 **buf, size_t *bufsize) { struct sc_asn1_entry asn1_com_data_attr[4], asn1_type_data_attr[2], asn1_data[2]; struct sc_pkcs15_data_info *info; struct sc_asn1_pkcs15_object data_obj = { (struct sc_pkcs15_object *) obj, asn1_com_data_attr, NULL, asn1_type_data_attr }; size_t label_len; info = (struct sc_pkcs15_data_info *) obj->data; label_len = strnlen(info->app_label, 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); if (label_len) sc_format_asn1_entry(asn1_com_data_attr + 0, &info->app_label, &label_len, 1); if (sc_valid_oid(&info->app_oid)) sc_format_asn1_entry(asn1_com_data_attr + 1, &info->app_oid, NULL, 1); sc_format_asn1_entry(asn1_type_data_attr + 0, &info->path, NULL, 1); sc_format_asn1_entry(asn1_data + 0, &data_obj, NULL, 1); return sc_asn1_encode(ctx, asn1_data, buf, bufsize); } void sc_pkcs15_free_data_object(struct sc_pkcs15_data *data_object) { if (data_object == NULL) return; free(data_object->data); free(data_object); } void sc_pkcs15_free_data_info(struct sc_pkcs15_data_info *info) { if (info && info->data.value && info->data.len) free(info->data.value); free(info); } OpenSC-0.26.1/src/libopensc/pkcs15-din-66291.c000066400000000000000000000246651474147347300202360ustar00rootroot00000000000000/* * 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, NULL)) { 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; } OpenSC-0.26.1/src/libopensc/pkcs15-dnie.c000066400000000000000000000215071474147347300177060ustar00rootroot00000000000000/** * PKCS15 emulation layer for DNIe card. * * Copyright (C) 2011, Andre Zepezauer * Copyright (C) 2011, Juan Antonio Martinez * * 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 "libopensc/log.h" #include "libopensc/asn1.h" #include "libopensc/pkcs15.h" #include "libopensc/cwa14890.h" #include "libopensc/cwa-dnie.h" /* Card driver related */ #if defined(ENABLE_OPENSSL) && defined(ENABLE_SM) extern int dnie_match_card(struct sc_card *card); /* Helper functions to get the pkcs15 stuff bound. */ static int dump_ef(sc_card_t * card, const char *path, u8 * buf, size_t * buf_len) { int rv; sc_file_t *file = NULL; sc_path_t scpath; sc_format_path(path, &scpath); rv = sc_select_file(card, &scpath, &file); if (rv < 0) { sc_file_free(file); return rv; } if (file->size > *buf_len) { sc_file_free(file); return SC_ERROR_BUFFER_TOO_SMALL; } rv = sc_read_binary(card, 0, buf, file->size, 0); sc_file_free(file); if (rv < 0) return rv; *buf_len = rv; return SC_SUCCESS; } static const struct sc_asn1_entry c_asn1_odf[] = { {"privateKeys", SC_ASN1_STRUCT, SC_ASN1_CTX | 0 | SC_ASN1_CONS, 0, NULL, NULL}, {"publicKeys", SC_ASN1_STRUCT, SC_ASN1_CTX | 1 | SC_ASN1_CONS, 0, NULL, NULL}, {"trustedPublicKeys", SC_ASN1_STRUCT, SC_ASN1_CTX | 2 | SC_ASN1_CONS, 0, NULL, NULL}, {"secretKeys", SC_ASN1_STRUCT, SC_ASN1_CTX | 3 | SC_ASN1_CONS, 0, NULL, NULL}, {"certificates", SC_ASN1_STRUCT, SC_ASN1_CTX | 4 | SC_ASN1_CONS, 0, NULL, NULL}, {"trustedCertificates", SC_ASN1_STRUCT, SC_ASN1_CTX | 5 | SC_ASN1_CONS, 0, NULL, NULL}, {"usefulCertificates", SC_ASN1_STRUCT, SC_ASN1_CTX | 6 | SC_ASN1_CONS, 0, NULL, NULL}, {"dataObjects", SC_ASN1_STRUCT, SC_ASN1_CTX | 7 | SC_ASN1_CONS, 0, NULL, NULL}, {"authObjects", SC_ASN1_STRUCT, SC_ASN1_CTX | 8 | SC_ASN1_CONS, 0, NULL, NULL}, {NULL, 0, 0, 0, NULL, NULL} }; static const unsigned int odf_indexes[] = { SC_PKCS15_PRKDF, SC_PKCS15_PUKDF, SC_PKCS15_PUKDF_TRUSTED, SC_PKCS15_SKDF, SC_PKCS15_CDF, SC_PKCS15_CDF_TRUSTED, SC_PKCS15_CDF_USEFUL, SC_PKCS15_DODF, SC_PKCS15_AODF, }; static int parse_odf(const u8 * buf, size_t buflen, struct sc_pkcs15_card *p15card) { const u8 *p = buf; size_t left = buflen; int r, i, type; sc_path_t path; struct sc_asn1_entry asn1_obj_or_path[] = { {"path", SC_ASN1_PATH, SC_ASN1_CONS | SC_ASN1_SEQUENCE, 0, &path, NULL}, {NULL, 0, 0, 0, NULL, NULL} }; struct sc_asn1_entry asn1_odf[10]; sc_path_t path_prefix; sc_format_path("3F005015", &path_prefix); sc_copy_asn1_entry(c_asn1_odf, asn1_odf); for (i = 0; asn1_odf[i].name != NULL; i++) sc_format_asn1_entry(asn1_odf + i, asn1_obj_or_path, NULL, 0); while (left > 0) { r = sc_asn1_decode_choice(p15card->card->ctx, asn1_odf, p, left, &p, &left); if (r == SC_ERROR_ASN1_END_OF_CONTENTS) break; if (r < 0) return r; type = r; r = sc_pkcs15_make_absolute_path(&path_prefix, &path); if (r < 0) return r; r = sc_pkcs15_add_df(p15card, odf_indexes[type], &path); if (r) return r; } return 0; } static int sc_pkcs15emu_dnie_init(sc_pkcs15_card_t * p15card) { u8 buf[1024]; sc_pkcs15_df_t *df; sc_pkcs15_object_t *p15_obj; 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); /* Check for correct card driver (i.e. iso7816) */ if (strcmp(p15card->card->driver->short_name, "dnie") != 0) return SC_ERROR_WRONG_CARD; /* Check for correct card atr */ if (dnie_match_card(p15card->card) != 1) return SC_ERROR_WRONG_CARD; /* The two keys inside DNIe 3.0 needs login before performing any signature. * They are CKA_ALWAYS_AUTHENTICATE although they are not tagged like that. * For the moment caching is forced if 3.0 is detected to make it work properly. */ if (p15card->card->atr.value[15] >= DNIE_30_VERSION) { p15card->opts.use_pin_cache = 1; p15card->opts.pin_cache_counter = DNIE_30_CACHE_COUNTER; sc_log(ctx, "DNIe 3.0 detected - PKCS#15 options reset: 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); } /* 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); LOG_TEST_GOTO_ERR(ctx, rv, "Reading of EF.TOKENINFO failed."); rv = sc_pkcs15_parse_tokeninfo(p15card->card->ctx, p15card->tokeninfo, buf, len); 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) { rv = SC_ERROR_WRONG_CARD; LOG_TEST_GOTO_ERR(ctx, rv, "Wrong card."); } /* Load ODF */ rv = dump_ef(p15card->card, "3F0050155031", buf, &len); LOG_TEST_GOTO_ERR(ctx, rv, "Reading of ODF failed."); rv = parse_odf(buf, len, p15card); 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) { if (df->type == SC_PKCS15_PRKDF) { rv = sc_pkcs15_parse_df(p15card, df); if (rv != SC_SUCCESS) { sc_log(ctx, "Decoding of EF.PrKDF (%s) failed: %d", sc_print_path(&df->path), rv); } } if (df->type == SC_PKCS15_PUKDF) { rv = sc_pkcs15_parse_df(p15card, df); if (rv != SC_SUCCESS) { sc_log(ctx, "Decoding of EF.PuKDF (%s) failed: %d", sc_print_path(&df->path), rv); } } if (df->type == SC_PKCS15_CDF) { rv = sc_pkcs15_parse_df(p15card, df); if (rv != SC_SUCCESS) { sc_log(ctx, "Decoding of EF.CDF (%s) failed: %d", sc_print_path(&df->path), rv); } } if (df->type == SC_PKCS15_DODF) { rv = sc_pkcs15_parse_df(p15card, df); if (rv != SC_SUCCESS) { sc_log(ctx, "Decoding of EF.DODF (%s) failed: %d", sc_print_path(&df->path), rv); } } } /* Perform required fixes */ p15_obj = p15card->obj_list; while (p15_obj != NULL) { /* Add missing 'auth_id' to private objects */ if ((p15_obj->flags & SC_PKCS15_CO_FLAG_PRIVATE) && (p15_obj->auth_id.len == 0)) { p15_obj->auth_id.value[0] = 0x01; p15_obj->auth_id.len = 1; }; /* Set path count to -1 for public certificates, as they will need to be decompressed and read_binary()'d, so we make sure we end up reading the file->size and not the path->count which is the compressed size on newer DNIe versions */ if ( p15_obj->df && (p15_obj->df->type == SC_PKCS15_CDF) ) { p15_info = (struct sc_pkcs15_cert_info *) p15_obj ->data; p15_info ->path.count = -1; } /* Remove found public keys as cannot be read_binary()'d */ if ( p15_obj->df && (p15_obj->df->type == SC_PKCS15_PUKDF) ) { sc_pkcs15_object_t *puk = p15_obj; p15_obj = p15_obj->next; sc_pkcs15_remove_object(p15card, puk); sc_pkcs15_free_object(puk); } else { p15_obj = p15_obj->next; } } 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 /****************************************/ /* public functions for in-built module */ /****************************************/ int sc_pkcs15emu_dnie_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 defined(ENABLE_OPENSSL) && defined(ENABLE_SM) /* check for proper card */ r = dnie_match_card(p15card->card); if (r == 0) LOG_FUNC_RETURN(ctx, SC_ERROR_WRONG_CARD); /* ok: initialize and return */ LOG_FUNC_RETURN(ctx, sc_pkcs15emu_dnie_init(p15card)); #else r = SC_ERROR_WRONG_CARD; LOG_FUNC_RETURN(ctx, r); #endif } OpenSC-0.26.1/src/libopensc/pkcs15-dtrust.c000066400000000000000000000056161474147347300203170ustar00rootroot00000000000000/* * 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 */ #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); 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: 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; prkey_info->field_length = 256; } 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_V4_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; 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); } OpenSC-0.26.1/src/libopensc/pkcs15-emulator-filter.c000066400000000000000000000070651474147347300221050ustar00rootroot00000000000000/* * 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); } OpenSC-0.26.1/src/libopensc/pkcs15-emulator-filter.h000066400000000000000000000022011474147347300220750ustar00rootroot00000000000000/* * 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); OpenSC-0.26.1/src/libopensc/pkcs15-eoi.c000066400000000000000000000131321474147347300175360ustar00rootroot00000000000000/* * 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 establisahed). */ 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; 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) { 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 OpenSC-0.26.1/src/libopensc/pkcs15-esinit.c000066400000000000000000000046421474147347300202630ustar00rootroot00000000000000/* * 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 */ /* Initially written by Weitao Sun (weitao@ftsafe.com) 2008*/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "internal.h" #include "pkcs15.h" #include "cardctl.h" #define MANU_ID "entersafe" static int entersafe_detect_card( sc_pkcs15_card_t *p15card) { sc_card_t *card = p15card->card; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); /* check if we have the correct card OS */ if (strcmp(card->name, "entersafe")) return SC_ERROR_WRONG_CARD; return SC_SUCCESS; } static int sc_pkcs15emu_entersafe_init( sc_pkcs15_card_t *p15card) { int r; char buf[256]; sc_card_t *card = p15card->card; sc_serial_number_t serial; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); /* 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; set_string(&p15card->tokeninfo->serial_number, buf); if (!p15card->tokeninfo->serial_number) return SC_ERROR_INTERNAL; /* the manufacturer ID, in this case Giesecke & Devrient GmbH */ 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; } return SC_SUCCESS; } int sc_pkcs15emu_entersafe_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *aid) { SC_FUNC_CALLED(p15card->card->ctx, SC_LOG_DEBUG_VERBOSE); if (entersafe_detect_card(p15card)) return SC_ERROR_WRONG_CARD; return sc_pkcs15emu_entersafe_init(p15card); } OpenSC-0.26.1/src/libopensc/pkcs15-esteid2018.c000066400000000000000000000166441474147347300205650ustar00rootroot00000000000000/* * 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 sc_pkcs15emu_esteid2018_init(sc_pkcs15_card_t *p15card) { sc_card_t *card = p15card->card; u8 buff[11]; int r, i; size_t field_length = 0, taglen, j; sc_path_t tmppath; set_string(&p15card->tokeninfo->label, "ID-kaart"); set_string(&p15card->tokeninfo->manufacturer_id, "IDEMIA"); /* 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 unsigned char *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 (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); p15card->tokeninfo->serial_number = memcpy(p15card->tokeninfo->serial_number, tag, taglen); p15card->tokeninfo->serial_number[taglen] = '\0'; p15card->tokeninfo->flags = SC_PKCS15_TOKEN_READONLY; /* add certificates */ for (i = 0; i < 2; i++) { const char *esteid_cert_names[2] = {"Isikutuvastus", "Allkirjastamine"}; const char *esteid_cert_paths[2] = {"3f00:adf1:3401", "3f00:adf2:341f"}; const u8 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)); strlcpy(cert_obj.label, esteid_cert_names[i], sizeof(cert_obj.label)); sc_format_path(esteid_cert_paths[i], &cert_info.path); cert_info.id.value[0] = esteid_cert_ids[i]; cert_info.id.len = 1; 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; 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); } /* add pins */ for (i = 0; i < 3; i++) { const char *esteid_pin_names[3] = {"PIN1", "PIN2", "PUK"}; const size_t esteid_pin_min[3] = {4, 5, 8}; const int esteid_pin_ref[3] = {0x01, 0x85, 0x02}; const u8 esteid_pin_authid[3] = {1, 2, 3}; const char *esteid_pin_path[3] = {"3F00", "3F00ADF2", "3F00"}; const unsigned int esteid_pin_flags[3] = { SC_PKCS15_PIN_FLAG_NEEDS_PADDING | SC_PKCS15_PIN_FLAG_INITIALIZED, SC_PKCS15_PIN_FLAG_NEEDS_PADDING | SC_PKCS15_PIN_FLAG_INITIALIZED | SC_PKCS15_PIN_FLAG_LOCAL, SC_PKCS15_PIN_FLAG_NEEDS_PADDING | SC_PKCS15_PIN_FLAG_INITIALIZED | 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)); sc_format_path(esteid_pin_path[i], &pin_info.path); 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 = 0xFF; pin_info.tries_left = 3; 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); 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++) { const u8 prkey_pin[2] = {1, 2}; const char *prkey_name[2] = {"Isikutuvastus", "Allkirjastamine"}; const char *prkey_path[2] = {"3F00:ADF1", "3F00:ADF2"}; const unsigned int prkey_usage[2] = {SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_DERIVE, SC_PKCS15_PRKEY_USAGE_NONREPUDIATION}; const int prkey_consent[2] = {0, 1}; 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_format_path(prkey_path[i], &prkey_info.path); prkey_info.id.len = 1; prkey_info.id.value[0] = prkey_pin[i]; prkey_info.native = 1; prkey_info.key_reference = i + 1; prkey_info.field_length = field_length; prkey_info.usage = prkey_usage[i]; 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 = prkey_consent[i]; prkey_obj.flags = SC_PKCS15_CO_FLAG_PRIVATE; 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) return sc_pkcs15emu_esteid2018_init(p15card); return SC_ERROR_WRONG_CARD; } OpenSC-0.26.1/src/libopensc/pkcs15-gemsafeV1.c000066400000000000000000000455371474147347300206160ustar00rootroot00000000000000/* * 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 */ /* Initially written by David Mattes */ /* Support for multiple key containers by Lukas Wunner */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "internal.h" #include "pkcs15.h" #define MANU_ID "Gemplus" #define APPLET_NAME "GemSAFE V1" #define DRIVER_SERIAL_NUMBER "v0.9" #define GEMSAFE_APP_PATH "3F001600" #define GEMSAFE_PATH "3F0016000004" /* Apparently, the Applet max read "quanta" is 248 bytes * Gemalto ClassicClient reads files in chunks of 238 bytes */ #define GEMSAFE_READ_QUANTUM 248 #define GEMSAFE_MAX_OBJLEN 28672 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); 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); 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); typedef struct cdata_st { char *label; int authority; const char *path; int index; int count; const char *id; int obj_flags; } cdata; const unsigned int gemsafe_cert_max = 12; cdata gemsafe_cert[] = { {"DS certificate #1", 0, GEMSAFE_PATH, 0, 0, "45", SC_PKCS15_CO_FLAG_MODIFIABLE}, {"DS certificate #2", 0, GEMSAFE_PATH, 0, 0, "46", SC_PKCS15_CO_FLAG_MODIFIABLE}, {"DS certificate #3", 0, GEMSAFE_PATH, 0, 0, "47", SC_PKCS15_CO_FLAG_MODIFIABLE}, {"DS certificate #4", 0, GEMSAFE_PATH, 0, 0, "48", SC_PKCS15_CO_FLAG_MODIFIABLE}, {"DS certificate #5", 0, GEMSAFE_PATH, 0, 0, "49", SC_PKCS15_CO_FLAG_MODIFIABLE}, {"DS certificate #6", 0, GEMSAFE_PATH, 0, 0, "50", SC_PKCS15_CO_FLAG_MODIFIABLE}, {"DS certificate #7", 0, GEMSAFE_PATH, 0, 0, "51", SC_PKCS15_CO_FLAG_MODIFIABLE}, {"DS certificate #8", 0, GEMSAFE_PATH, 0, 0, "52", SC_PKCS15_CO_FLAG_MODIFIABLE}, {"DS certificate #9", 0, GEMSAFE_PATH, 0, 0, "53", SC_PKCS15_CO_FLAG_MODIFIABLE}, {"DS certificate #10", 0, GEMSAFE_PATH, 0, 0, "54", SC_PKCS15_CO_FLAG_MODIFIABLE}, {"DS certificate #11", 0, GEMSAFE_PATH, 0, 0, "55", SC_PKCS15_CO_FLAG_MODIFIABLE}, {"DS certificate #12", 0, GEMSAFE_PATH, 0, 0, "56", SC_PKCS15_CO_FLAG_MODIFIABLE}, }; typedef struct pdata_st { const u8 atr[SC_MAX_ATR_SIZE]; const size_t atr_len; const char *id; const char *label; const char *path; const int ref; const int type; const unsigned int maxlen; const unsigned int minlen; const int flags; const int tries_left; const char pad_char; const int obj_flags; } pindata; const unsigned int gemsafe_pin_max = 2; const pindata gemsafe_pin[] = { /* ATR-specific PIN policies, first match found is used: */ { {0x3B, 0x7D, 0x96, 0x00, 0x00, 0x80, 0x31, 0x80, 0x65, 0xB0, 0x83, 0x11, 0x48, 0xC8, 0x83, 0x00, 0x90, 0x00}, 18, "01", "DS pin", GEMSAFE_PATH, 0x01, SC_PKCS15_PIN_TYPE_ASCII_NUMERIC, 8, 4, SC_PKCS15_PIN_FLAG_NEEDS_PADDING | SC_PKCS15_PIN_FLAG_LOCAL, 3, 0x00, SC_PKCS15_CO_FLAG_MODIFIABLE | SC_PKCS15_CO_FLAG_PRIVATE }, /* default PIN policy comes last: */ { { 0 }, 0, "01", "DS pin", GEMSAFE_PATH, 0x01, SC_PKCS15_PIN_TYPE_BCD, 16, 6, SC_PKCS15_PIN_FLAG_NEEDS_PADDING | SC_PKCS15_PIN_FLAG_LOCAL, 3, 0xFF, SC_PKCS15_CO_FLAG_MODIFIABLE | SC_PKCS15_CO_FLAG_PRIVATE } }; typedef struct prdata_st { const char *id; char *label; unsigned int modulus_len; int usage; const char *path; int ref; const char *auth_id; int obj_flags; } prdata; #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 prdata gemsafe_prkeys[] = { { "45", "DS key #1", 1024, USAGE_AUT, GEMSAFE_PATH, 0x03, "01", SC_PKCS15_CO_FLAG_PRIVATE}, { "46", "DS key #2", 1024, USAGE_AUT, GEMSAFE_PATH, 0x04, "01", SC_PKCS15_CO_FLAG_PRIVATE}, { "47", "DS key #3", 1024, USAGE_AUT, GEMSAFE_PATH, 0x05, "01", SC_PKCS15_CO_FLAG_PRIVATE}, { "48", "DS key #4", 1024, USAGE_AUT, GEMSAFE_PATH, 0x06, "01", SC_PKCS15_CO_FLAG_PRIVATE}, { "49", "DS key #5", 1024, USAGE_AUT, GEMSAFE_PATH, 0x07, "01", SC_PKCS15_CO_FLAG_PRIVATE}, { "50", "DS key #6", 1024, USAGE_AUT, GEMSAFE_PATH, 0x08, "01", SC_PKCS15_CO_FLAG_PRIVATE}, { "51", "DS key #7", 1024, USAGE_AUT, GEMSAFE_PATH, 0x09, "01", SC_PKCS15_CO_FLAG_PRIVATE}, { "52", "DS key #8", 1024, USAGE_AUT, GEMSAFE_PATH, 0x0a, "01", SC_PKCS15_CO_FLAG_PRIVATE}, { "53", "DS key #9", 1024, USAGE_AUT, GEMSAFE_PATH, 0x0b, "01", SC_PKCS15_CO_FLAG_PRIVATE}, { "54", "DS key #10", 1024, USAGE_AUT, GEMSAFE_PATH, 0x0c, "01", SC_PKCS15_CO_FLAG_PRIVATE}, { "55", "DS key #11", 1024, USAGE_AUT, GEMSAFE_PATH, 0x0d, "01", SC_PKCS15_CO_FLAG_PRIVATE}, { "56", "DS key #12", 1024, USAGE_AUT, GEMSAFE_PATH, 0x0e, "01", SC_PKCS15_CO_FLAG_PRIVATE}, }; static int gemsafe_get_cert_len(sc_card_t *card) { int r; u8 ibuf[GEMSAFE_MAX_OBJLEN]; u8 *iptr; struct sc_path path; struct sc_file *file; 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 */ 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!) */ objlen = (((size_t) ibuf[0]) << 8) | ibuf[1]; sc_log(card->ctx, "Stored object is of size: %"SC_FORMAT_LEN_SIZE_T"u", objlen); if (objlen < 1 || objlen > GEMSAFE_MAX_OBJLEN) { sc_log(card->ctx, "Invalid object size: %"SC_FORMAT_LEN_SIZE_T"u", objlen); return SC_ERROR_INTERNAL; } /* It looks like the first thing in the block is a table of * which keys are allocated. The table is small and is in the * first 248 bytes. Example for a card with 10 key containers: * 01 f0 00 03 03 b0 00 03 <= 1st key unallocated * 01 f0 00 04 03 b0 00 04 <= 2nd key unallocated * 01 fe 14 00 05 03 b0 00 05 <= 3rd key allocated * 01 fe 14 01 06 03 b0 00 06 <= 4th key allocated * 01 f0 00 07 03 b0 00 07 <= 5th key unallocated * ... * 01 f0 00 0c 03 b0 00 0c <= 10th key unallocated * For allocated keys, the fourth byte seems to indicate the * default key and the fifth byte indicates the key_ref of * the private key. */ ind = 2; /* skip length */ 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", i+1, gemsafe_prkeys[i].ref); ind += 9; } else { gemsafe_prkeys[i].label = NULL; gemsafe_cert[i].label = NULL; sc_log(card->ctx, "Key container %d is unallocated", i+1); ind += 8; } i++; } /* Delete additional key containers from the data structures if * this card can't accommodate them. */ for (; i < gemsafe_cert_max; i++) { gemsafe_prkeys[i].label = NULL; gemsafe_cert[i].label = NULL; } /* Read entire file, then dissect in memory. * Gemalto ClassicClient seems to do it the same way. */ iptr = ibuf + read_len; while ((size_t)(iptr - ibuf) < objlen) { 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; } 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. */ i = 0; while (ind < objlen - 1) { if (ibuf[ind] == 0x30 && ibuf[ind+1] == 0x82) { /* Find next allocated key container */ while (i < gemsafe_cert_max && gemsafe_cert[i].label == NULL) i++; if (i == gemsafe_cert_max) { sc_log(card->ctx, "Warning: Found orphaned certificate at offset %d", ind); return SC_SUCCESS; } /* DER cert len is encoded this way */ if (ind+3 >= sizeof ibuf) return SC_ERROR_INVALID_DATA; certlen = ((((int)ibuf[ind + 2]) << 8) | ibuf[ind + 3]) + 4; sc_log(card->ctx, "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; ind += certlen; i++; } else ind++; } /* Delete additional key containers from the data structures if * they're missing on the card. */ for (; i < gemsafe_cert_max; i++) { if (gemsafe_cert[i].label) { sc_log(card->ctx, "Warning: Certificate of key container %d is missing", i+1); gemsafe_prkeys[i].label = NULL; gemsafe_cert[i].label = NULL; } } return SC_SUCCESS; } static int gemsafe_detect_card( sc_pkcs15_card_t *p15card) { if (strcmp(p15card->card->name, "GemSAFE V1")) return SC_ERROR_WRONG_CARD; return SC_SUCCESS; } static int sc_pkcs15emu_gemsafeV1_init( sc_pkcs15_card_t *p15card) { int r; unsigned int i; struct sc_path path; struct sc_file *file = NULL; struct sc_card *card = p15card->card; struct sc_apdu apdu; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; sc_log(p15card->card->ctx, "Setting pkcs15 parameters"); set_string(&p15card->tokeninfo->label, APPLET_NAME); if (!p15card->tokeninfo->label) return SC_ERROR_INTERNAL; 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; } /* 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 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 || r != SC_SUCCESS) { sc_pkcs15_card_clear(p15card); return SC_ERROR_INTERNAL; } /* the manufacturer ID, in this case GemPlus */ set_string(&p15card->tokeninfo->manufacturer_id, MANU_ID); if (!p15card->tokeninfo->manufacturer_id) { sc_pkcs15_card_clear(p15card); return SC_ERROR_INTERNAL; } /* determine allocated key containers and length of certificates */ r = gemsafe_get_cert_len(card); if (r != SC_SUCCESS) { sc_pkcs15_card_clear(p15card); return SC_ERROR_INTERNAL; } /* set certs */ sc_log(p15card->card->ctx, "Setting certificates"); for (i = 0; i < gemsafe_cert_max; i++) { struct sc_pkcs15_id p15Id; struct sc_path path; if (gemsafe_cert[i].label == NULL) continue; sc_format_path(gemsafe_cert[i].path, &path); sc_pkcs15_format_id(gemsafe_cert[i].id, &p15Id); path.index = gemsafe_cert[i].index; path.count = gemsafe_cert[i].count; sc_pkcs15emu_add_cert(p15card, SC_PKCS15_TYPE_CERT_X509, gemsafe_cert[i].authority, &path, &p15Id, gemsafe_cert[i].label, gemsafe_cert[i].obj_flags); } /* set gemsafe_pin */ sc_log(p15card->card->ctx, "Setting PIN"); for (i=0; i < gemsafe_pin_max; i++) { struct sc_pkcs15_id p15Id; struct sc_path path; sc_pkcs15_format_id(gemsafe_pin[i].id, &p15Id); sc_format_path(gemsafe_pin[i].path, &path); if (gemsafe_pin[i].atr_len == 0 || (gemsafe_pin[i].atr_len == p15card->card->atr.len && memcmp(p15card->card->atr.value, gemsafe_pin[i].atr, p15card->card->atr.len) == 0)) { sc_pkcs15emu_add_pin(p15card, &p15Id, gemsafe_pin[i].label, &path, gemsafe_pin[i].ref, gemsafe_pin[i].type, gemsafe_pin[i].minlen, gemsafe_pin[i].maxlen, gemsafe_pin[i].flags, gemsafe_pin[i].tries_left, gemsafe_pin[i].pad_char, gemsafe_pin[i].obj_flags); break; } }; /* set private keys */ sc_log(p15card->card->ctx, "Setting private keys"); for (i = 0; i < gemsafe_cert_max; i++) { struct sc_pkcs15_id p15Id, authId, *pauthId; struct sc_path path; int key_ref = 0x03; if (gemsafe_prkeys[i].label == NULL) continue; sc_pkcs15_format_id(gemsafe_prkeys[i].id, &p15Id); if (gemsafe_prkeys[i].auth_id) { sc_pkcs15_format_id(gemsafe_prkeys[i].auth_id, &authId); pauthId = &authId; } else pauthId = NULL; sc_format_path(gemsafe_prkeys[i].path, &path); /* * The key ref may be different for different sites; * by adding flags=n where the low order 4 bits can be * the key ref we can force it. */ if ( p15card->card->flags & 0x0F) { key_ref = p15card->card->flags & 0x0F; sc_log(p15card->card->ctx, "Overriding key_ref %d with %d\n", gemsafe_prkeys[i].ref, key_ref); } else key_ref = gemsafe_prkeys[i].ref; sc_pkcs15emu_add_prkey(p15card, &p15Id, gemsafe_prkeys[i].label, SC_PKCS15_TYPE_PRKEY_RSA, gemsafe_prkeys[i].modulus_len, gemsafe_prkeys[i].usage, &path, key_ref, pauthId, gemsafe_prkeys[i].obj_flags); } /* select the application DF */ 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) { sc_pkcs15_card_clear(p15card); return SC_ERROR_INTERNAL; } /* set the application DF */ 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) { if (gemsafe_detect_card(p15card)) return SC_ERROR_WRONG_CARD; return sc_pkcs15emu_gemsafeV1_init(p15card); } static sc_pkcs15_df_t * sc_pkcs15emu_get_df(sc_pkcs15_card_t *p15card, unsigned int type) { sc_pkcs15_df_t *df; sc_file_t *file; int created = 0; while (1) { for (df = p15card->df_list; df; df = df->next) { if (df->type == type) { if (created) df->enumerated = 1; return df; } } assert(created == 0); file = sc_file_new(); if (!file) return NULL; sc_format_path("11001101", &file->path); sc_pkcs15_add_df(p15card, type, &file->path); sc_file_free(file); created++; } } static int sc_pkcs15emu_add_object(sc_pkcs15_card_t *p15card, int type, const char *label, void *data, const sc_pkcs15_id_t *auth_id, int obj_flags) { sc_pkcs15_object_t *obj; int df_type; obj = calloc(1, sizeof(*obj)); obj->type = type; obj->data = data; if (label) strncpy(obj->label, label, sizeof(obj->label)-1); obj->flags = obj_flags; if (auth_id) obj->auth_id = *auth_id; switch (type & SC_PKCS15_TYPE_CLASS_MASK) { case SC_PKCS15_TYPE_AUTH: df_type = SC_PKCS15_AODF; break; case SC_PKCS15_TYPE_PRKEY: df_type = SC_PKCS15_PRKDF; break; case SC_PKCS15_TYPE_PUBKEY: df_type = SC_PKCS15_PUKDF; break; case SC_PKCS15_TYPE_CERT: df_type = SC_PKCS15_CDF; break; default: sc_log(p15card->card->ctx, "Unknown PKCS15 object type %d", type); free(obj); return SC_ERROR_INVALID_ARGUMENTS; } obj->df = sc_pkcs15emu_get_df(p15card, df_type); sc_pkcs15_add_object(p15card, obj); return 0; } 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; info = calloc(1, sizeof(*info)); if (!info) LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_OUT_OF_MEMORY); info->auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; info->auth_method = SC_AC_CHV; 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; return sc_pkcs15emu_add_object(p15card, SC_PKCS15_TYPE_AUTH_PIN, label, info, NULL, obj_flags); } 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) { sc_pkcs15_cert_info_t *info; info = calloc(1, sizeof(*info)); if (!info) { LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_OUT_OF_MEMORY); } info->id = *id; info->authority = authority; if (path) info->path = *path; return sc_pkcs15emu_add_object(p15card, type, label, info, NULL, obj_flags); } 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; info = calloc(1, sizeof(*info)); if (!info) { LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_OUT_OF_MEMORY); } info->id = *id; info->modulus_length = modulus_length; info->usage = usage; info->native = 1; info->access_flags = SC_PKCS15_PRKEY_ACCESS_SENSITIVE | SC_PKCS15_PRKEY_ACCESS_ALWAYSSENSITIVE | SC_PKCS15_PRKEY_ACCESS_NEVEREXTRACTABLE | SC_PKCS15_PRKEY_ACCESS_LOCAL; info->key_reference = ref; if (path) info->path = *path; return sc_pkcs15emu_add_object(p15card, type, label, info, auth_id, obj_flags); } /* SC_IMPLEMENT_DRIVER_VERSION("0.9.4") */ OpenSC-0.26.1/src/libopensc/pkcs15-gids.c000066400000000000000000000207121474147347300177120ustar00rootroot00000000000000/* * pkcs15-gids.c: Support for GIDS smart cards. * * Copyright (C) 2015 Vincent Le Toux (My Smart Logon) * * 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" #include "common/compat_strlcpy.h" #include "cardctl.h" #ifdef ENABLE_ZLIB #include "card-gids.h" /* * Add a key from a minidriver container */ static int sc_pkcs15emu_gids_add_prkey(sc_pkcs15_card_t * p15card, sc_cardctl_gids_get_container_t *container) { sc_card_t *card = p15card->card; sc_pkcs15_prkey_info_t prkey_info; sc_pkcs15_object_t prkey_obj; sc_pkcs15_pubkey_info_t pubkey_info; sc_pkcs15_object_t pubkey_obj; sc_pkcs15_cert_info_t cert_info; sc_pkcs15_object_t cert_obj; int r; char ch_tmp[10]; sc_log(card->ctx, "Got args: containerIndex=%"SC_FORMAT_LEN_SIZE_T"x\n", container->containernum); memset(&prkey_info, 0, sizeof(prkey_info)); memset(&prkey_obj, 0, sizeof(prkey_obj)); prkey_info.id.len = 1; prkey_info.id.value[0] = container->containernum; prkey_info.modulus_length = container->module_length; prkey_info.usage = container->prvusage; prkey_info.native = 1; 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; prkey_obj.auth_id.len = 1; prkey_obj.auth_id.value[0] = 0x80; r = sc_pkcs15emu_add_rsa_prkey(p15card, &prkey_obj, &prkey_info); 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)); strlcpy(pubkey_obj.label, container->label, sizeof(pubkey_obj.label)); snprintf(ch_tmp, sizeof(ch_tmp), "3FFFB0%02X", prkey_info.key_reference); sc_format_path(ch_tmp, &pubkey_info.path); pubkey_info.native = 1; pubkey_info.key_reference = prkey_info.key_reference; pubkey_info.modulus_length = prkey_info.modulus_length; pubkey_info.usage = container->pubusage; pubkey_info.id = prkey_info.id; r = sc_pkcs15emu_add_rsa_pubkey(p15card, &pubkey_obj, &pubkey_info); 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)); memset(&cert_obj, 0, sizeof(cert_obj)); cert_info.id = prkey_info.id; cert_info.path.count = -1; cert_info.path = container->certificatepath; strlcpy(cert_obj.label, container->label, sizeof(cert_obj.label)); r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info); LOG_TEST_RET(card->ctx, r, "Could not add certificate"); } else { sc_log(card->ctx, "No certificate found"); } return SC_SUCCESS; } /* * Initialize PKCS#15 emulation with user PIN, private keys, certificate and data objects * */ static int sc_pkcs15emu_gids_init (sc_pkcs15_card_t * p15card) { sc_card_t *card = p15card->card; int r; size_t i; struct sc_pkcs15_auth_info pin_info; struct sc_pkcs15_object pin_obj; struct sc_pin_cmd_data pin_cmd_data; size_t recordsnum; int has_puk; r = sc_card_ctl(card, SC_CARDCTL_GIDS_GET_ALL_CONTAINERS, &recordsnum); LOG_TEST_RET(card->ctx, r, "unable to get the containers. Uninitialized card ?"); r = sc_card_ctl(card, SC_CARDCTL_GET_SERIALNR, NULL); 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); } sc_bin_to_hex(card->serialnr.value, card->serialnr.len, p15card->tokeninfo->serial_number, card->serialnr.len *2 +1, 0); if (p15card->tokeninfo->label == NULL) { p15card->tokeninfo->label = strdup("GIDS card"); 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)) { free(p15card->tokeninfo->manufacturer_id); p15card->tokeninfo->manufacturer_id = NULL; } if (p15card->tokeninfo->manufacturer_id == NULL) { p15card->tokeninfo->manufacturer_id = strdup("www.mysmartlogon.com"); 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; } else if (p15card->card->type == SC_CARD_TYPE_GIDS_V1) { p15card->tokeninfo->version = 1; } 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] = 0x80; pin_info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; pin_info.attrs.pin.reference = 0x80; pin_info.attrs.pin.flags = SC_PKCS15_PIN_FLAG_LOCAL|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 = 15; pin_info.attrs.pin.pad_char = '\0'; pin_info.tries_left = -1; pin_info.max_tries = -1; memset(&pin_cmd_data, 0, sizeof(pin_cmd_data)); pin_cmd_data.cmd = SC_PIN_CMD_GET_INFO; 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); if (r == SC_SUCCESS) { pin_info.max_tries = pin_cmd_data.pin1.max_tries; pin_info.tries_left = pin_cmd_data.pin1.tries_left; } strlcpy(pin_obj.label, "UserPIN", sizeof(pin_obj.label)); pin_obj.flags = SC_PKCS15_CO_FLAG_PRIVATE|SC_PKCS15_CO_FLAG_MODIFIABLE; /* * check whether PUK is available on this card and then optionally * link PIN with PUK. */ pin_cmd_data.pin_reference = 0x81; has_puk = sc_pin_cmd(card, &pin_cmd_data, NULL) == 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); LOG_TEST_GOTO_ERR(card->ctx, r, "unable to sc_pkcs15emu_add_pin_obj"); if (has_puk) { pin_info.auth_id.value[0] = 0x81; pin_info.attrs.pin.flags = SC_PKCS15_PIN_FLAG_LOCAL|SC_PKCS15_PIN_FLAG_INITIALIZED | SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN; pin_info.attrs.pin.reference = 0x81; pin_info.max_tries = pin_cmd_data.pin1.max_tries; pin_info.tries_left = pin_cmd_data.pin1.tries_left; 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); 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); 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; memset(&container, 0, sizeof(sc_cardctl_gids_get_container_t)); container.containernum = i; r = sc_card_ctl(card, SC_CARDCTL_GIDS_GET_CONTAINER_DETAIL, &container); if (r < 0) { // one of the container information couldn't be retrieved // ignore it continue; } 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) { 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) { return SC_ERROR_WRONG_CARD; } #endif OpenSC-0.26.1/src/libopensc/pkcs15-iasecc.c000066400000000000000000000166511474147347300202220ustar00rootroot00000000000000/* * PKCS15 emulation layer for IAS/ECC card. * * 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 #ifdef ENABLE_OPENSSL #include #endif #include "internal.h" #include "pkcs15.h" #include "../pkcs15init/pkcs15-iasecc.h" #include "iasecc.h" #include "aux-data.h" #define IASECC_GEMALTO_MD_APPLICATION_NAME "CSP" #define IASECC_GEMALTO_MD_DEFAULT_CONT_LABEL "Default Key Container" static int _iasecc_md_update_keyinfo(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *dobj, int default_cont) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_prkey_info *prkey_info = NULL; struct sc_pkcs15_object *prkey_object = NULL; struct sc_pkcs15_data *ddata = NULL; 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); 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"); offs = 0; if (*(ddata->data + offs++) != 0x01) { sc_pkcs15_free_data_object(ddata); LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); } id.len = *(ddata->data + offs++); memcpy(id.value, ddata->data + offs, id.len); offs += (int) id.len; if (*(ddata->data + offs++) != 0x02) { sc_pkcs15_free_data_object(ddata); LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); } if (*(ddata->data + offs++) != 0x01) { sc_pkcs15_free_data_object(ddata); LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); } flags = *(ddata->data + offs); if (default_cont) flags |= SC_MD_CONTAINER_MAP_DEFAULT_CONTAINER; sc_pkcs15_free_data_object(ddata); rv = sc_pkcs15_find_prkey_by_id(p15card, &id, &prkey_object); LOG_TEST_RET(ctx, rv, "Find related PrKey error"); prkey_info = (struct sc_pkcs15_prkey_info *)prkey_object->data; if (prkey_info->aux_data == NULL) { rv = sc_aux_data_allocate(ctx, &prkey_info->aux_data, NULL); LOG_TEST_RET(ctx, rv, "Cannot allocate MD auxiliary data"); } rv = sc_aux_data_set_md_guid(ctx, prkey_info->aux_data, dobj->label); LOG_TEST_RET(ctx, rv, "Cannot set MD CMAP Guid"); rv = sc_aux_data_set_md_flags(ctx, prkey_info->aux_data, flags); LOG_TEST_RET(ctx, rv, "Cannot set MD CMAP record flags"); LOG_FUNC_RETURN(ctx, rv); } /* * 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->ctx; struct sc_pkcs15_object *dobjs[32]; struct sc_pkcs15_data *default_guid = NULL; int rv, ii, 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"); 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_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, private_obj, &default_guid); LOG_TEST_RET(ctx, rv, "Failed to read 'default container' DATA object data"); break; } } for(ii=0; iidata; int default_cont = 0; if (strcmp(dinfo->app_label, IASECC_GEMALTO_MD_APPLICATION_NAME)) continue; if (!strcmp(dobjs[ii]->label, IASECC_GEMALTO_MD_DEFAULT_CONT_LABEL)) continue; if (default_guid) if (strlen(dobjs[ii]->label) == default_guid->data_len) if (!memcmp(dobjs[ii]->label, default_guid->data, default_guid->data_len)) default_cont = 1; rv = _iasecc_md_update_keyinfo(p15card, dobjs[ii], default_cont); LOG_TEST_RET(ctx, rv, "Cannot update key MD info"); } sc_pkcs15_free_data_object(default_guid); LOG_FUNC_RETURN(ctx, rv); } static int iasecc_pkcs15emu_detect_card(sc_pkcs15_card_t *p15card) { if (p15card->card->type < SC_CARD_TYPE_IASECC_BASE) return SC_ERROR_WRONG_CARD; if (p15card->card->type > SC_CARD_TYPE_IASECC_BASE + 10) return SC_ERROR_WRONG_CARD; return SC_SUCCESS; } static int sc_pkcs15emu_iasecc_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 = _iasecc_parse_df; LOG_FUNC_RETURN(ctx, rv); } int sc_pkcs15emu_iasecc_init_ex(struct sc_pkcs15_card *p15card, struct sc_aid *aid) { if (iasecc_pkcs15emu_detect_card(p15card)) return SC_ERROR_WRONG_CARD; return sc_pkcs15emu_iasecc_init(p15card, aid); } OpenSC-0.26.1/src/libopensc/pkcs15-idprime.c000066400000000000000000000314511474147347300204170ustar00rootroot00000000000000/* * 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); } OpenSC-0.26.1/src/libopensc/pkcs15-itacns.c000066400000000000000000000561561474147347300202600ustar00rootroot00000000000000/* * PKCS15 emulation layer for Italian CNS. * * Copyright (C) 2008, Emanuele Pucciarelli * Many snippets have been taken out from other PKCS15 emulation layer * modules in this directory; their copyright is their authors'. * * 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 */ /* * Specifications for the development of this driver come from: * http://www.servizidemografici.interno.it/sitoCNSD/documentazioneRicerca.do?metodo=contenutoDocumento&servizio=documentazione&ID_DOCUMENTO=1043 */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "internal.h" #include "pkcs15.h" #include "log.h" #include "cards.h" #include "itacns.h" #include "common/compat_strlcpy.h" #include "common/compat_strlcat.h" #ifdef ENABLE_OPENSSL #include #include #endif static const char path_serial[] = "10001003"; /* Manufacturers */ const char * itacns_mask_manufacturers[] = { "Unknown", "Kaitech", "Gemplus", "Ghirlanda", "Giesecke & Devrient", "IDEMIA (Oberthur)", "Orga", "Axalto", "Siemens", "STIncard", "GEP", "EPS Corp", "Athena", "Gemalto", }; const char * iso7816_ic_manufacturers[] = { "Unknown", "Motorola", "STMicroelectronics", "Hitachi", "NXP Semiconductors", "Infineon", "Cylinc", "Texas Instruments", "Fujitsu", "Matsushita", "NEC", "Oki", "Toshiba", "Mitsubishi", "Samsung", "Hynix", "LG", "Emosyn-EM", "INSIDE", "ORGA", "SHARP", "ATMEL", "EM Microelectronic-Marin", "KSW Microtec", "ZMD", "XICOR", "Sony", "Malaysia Microelectronic Solutions", "Emosyn", "Shanghai Fudan", "Magellan", "Melexis", "Renesas", "TAGSYS", "Transcore", "Shanghai belling", "Masktech", "Innovision", "Hitachi", "Cypak", "Ricoh", "ASK", "Unicore", "Dallas", "Impinj", "RightPlug Alliance", "Broadcom", "MStar", "BeeDar", "RFIDsec", "Schweizer Electronic", "AMIC Technology", "Mikron", "Fraunhofer", "IDS Microchip", "Kovio", "HMT Microelectronic", "Silicon Craft", "Advanced Film Device", "Nitecrest", "Verayo", "HID Gloval", "Productivity Engineering", "Austriamicrosystems", "Gemalto" }; /* Data files */ static const struct { const char *label; const char *path; int cie_only; } itacns_data_files[] = { { "EF_DatiProcessore", "3F0010001002", 0 }, { "EF_IDCarta", "3F0010001003", 0 }, { "EF_DatiSistema", "3F0010001004", 1 }, { "EF_DatiPersonali", "3F0011001102", 0 }, { "EF_DatiPersonali_Annotazioni", "3F0011001103", 1 }, { "EF_Impronte", "3F0011001104", 1 }, { "EF_Foto", "3F0011001104", 1 }, { "EF_DatiPersonaliAggiuntivi", "3F0012001201", 0 }, { "EF_MemoriaResidua", "3F0012001202", 0 }, { "EF_ServiziInstallati", "3F0012001203", 0 }, { "EF_INST_FILE", "3F0012004142", 0 }, { "EF_CardStatus", "3F003F02", 0 }, { "EF_GDO", "3F002F02", 0 }, { "EF_RootInstFile", "3F000405", 0 } }; /* * Utility functions */ 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, SC_LOG_DEBUG_NORMAL); sc_res = sc_select_file(p15card->card, path, NULL); if(sc_res != SC_SUCCESS) return sc_res; sc_res = sc_read_binary(p15card->card, 0, buf, buflen, 0); return sc_res; } /* * The following functions add objects to the card emulator. */ 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, size_t *modulus_len) { int r; /* const char *label = "Certificate"; */ sc_pkcs15_cert_info_t info; sc_pkcs15_object_t obj; #ifdef ENABLE_OPENSSL X509 *x509; sc_pkcs15_cert_t *cert; int private_obj; #endif SC_FUNC_CALLED(p15card->card->ctx, SC_LOG_DEBUG_NORMAL); if(type != SC_PKCS15_TYPE_CERT_X509) { 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)); info.id = *id; info.authority = authority; if (path) info.path = *path; strlcpy(obj.label, label, sizeof(obj.label)); /* If we have OpenSSL, read keyUsage */ #ifdef ENABLE_OPENSSL 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) { sc_log_openssl(p15card->card->ctx); return SC_SUCCESS; } X509_check_purpose(x509, -1, 0); if(X509_get_extension_flags(x509) & EXFLAG_KUSAGE) { *ext_info_ok = 1; *key_usage = X509_get_key_usage(x509); *x_key_usage = X509_get_extended_key_usage(x509); } OPENSSL_free(x509); #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, size_t modulus_len) { int r; sc_pkcs15_pubkey_info_t info; sc_pkcs15_object_t obj; SC_FUNC_CALLED(p15card->card->ctx, SC_LOG_DEBUG_NORMAL); memset(&info, 0, sizeof(info)); memset(&obj, 0, sizeof(obj)); info.id = *id; if (path) info.path = *path; info.usage = usage; info.key_reference = ref; strlcpy(obj.label, label, sizeof(obj.label)); obj.flags = obj_flags; info.modulus_length = modulus_len; r = sc_pkcs15emu_add_rsa_pubkey(p15card, &obj, &info); LOG_TEST_RET(p15card->card->ctx, r, "Could not add pub key"); return r; } static int itacns_add_prkey(sc_pkcs15_card_t *p15card, const sc_pkcs15_id_t *id, const char *label, 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, SC_LOG_DEBUG_NORMAL); if(type != SC_PKCS15_TYPE_PRKEY_RSA) { sc_log(p15card->card->ctx, "Cannot add a private key of a type other than RSA"); return 1; } 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 itacns_add_pin(sc_pkcs15_card_t *p15card, char *label, int id, int auth_id, int reference, sc_path_t *path, int flags) { struct sc_pkcs15_auth_info pin_info; struct sc_pkcs15_object pin_obj; 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; pin_info.auth_id.len = 1; pin_info.auth_id.value[0] = id; pin_info.attrs.pin.reference = reference; pin_info.attrs.pin.flags = flags; pin_info.attrs.pin.type = SC_PKCS15_PIN_TYPE_ASCII_NUMERIC; pin_info.attrs.pin.min_length = 5; pin_info.attrs.pin.stored_length = 8; pin_info.attrs.pin.max_length = 8; pin_info.attrs.pin.pad_char = 0xff; pin_info.logged_in = SC_PIN_STATE_UNKNOWN; if(path) pin_info.path = *path; memset(&pin_obj, 0, sizeof(pin_obj)); strlcpy(pin_obj.label, label, sizeof(pin_obj.label)); pin_obj.flags = SC_PKCS15_CO_FLAG_PRIVATE | (auth_id ? SC_PKCS15_CO_FLAG_MODIFIABLE : 0); if (auth_id) { pin_obj.auth_id.len = 1; pin_obj.auth_id.value[0] = auth_id; } else pin_obj.auth_id.len = 0; return sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info); } static int hextoint(char *src, unsigned int len) { char hex[16]; char *end; int res; if(len >= sizeof(hex)) return -1; strncpy(hex, src, len); hex[len] = '\0'; 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, 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 * structure's total size, in base 16. */ 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, f_issuing_date, f_expiry_date, f_last_name, f_first_name, f_birth_date, f_sex, f_height, f_codice_fiscale, f_citizenship_code, f_birth_township_code, f_birth_country, f_birth_certificate, f_residence_township_code, f_residence_address, f_expat_notes }; /* Read the fields up to f_first_name */ struct { int len; char value[256]; } fields[f_first_name+1]; int i=0; /* offset inside the file */ int f; /* field number */ if (file_size < 0) return -1; /* * This shouldn't happen, but let us be protected against wrong * or malicious cards */ if(file_size > (int)EF_personaldata_maxlen - (int)tlv_length_size) file_size = EF_personaldata_maxlen - tlv_length_size; memset(fields, 0, sizeof(fields)); for(f=0; f file_size) return -1; field_size = hextoint((char*) &file[i], 2); i += 2; if (field_size < 0 || i + field_size > file_size || field_size >= (int)sizeof(fields[f].value)) return -1; fields[f].len = field_size; strncpy(fields[f].value, &file[i], field_size); fields[f].value[field_size] = '\0'; i += field_size; } if (fields[f_first_name].len + fields[f_last_name].len + 1 >= name_len) return -1; /* 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; } static int itacns_add_data_files(sc_pkcs15_card_t *p15card) { const size_t array_size = sizeof(itacns_data_files)/sizeof(itacns_data_files[0]); unsigned int i; int rv; sc_pkcs15_data_t *p15_personaldata = NULL; 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; sc_pkcs15_data_info_t data; sc_pkcs15_object_t obj; if (itacns_data_files[i].cie_only && p15card->card->type != SC_CARD_TYPE_ITACNS_CIE_V2) continue; sc_format_path(itacns_data_files[i].path, &path); memset(&data, 0, sizeof(data)); memset(&obj, 0, sizeof(obj)); strlcpy(data.app_label, itacns_data_files[i].label, sizeof(data.app_label)); strlcpy(obj.label, itacns_data_files[i].label, sizeof(obj.label)); data.path = path; rv = sc_pkcs15emu_add_data_object(p15card, &obj, &data); LOG_TEST_RET(p15card->card->ctx, rv, "Could not add data file"); } /* * If we got this far, we can read the Personal Data file and glean * the user's full name. Thus we can use it to put together a * user-friendlier card name. */ memset(&dinfo, 0, sizeof(dinfo)); 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_log(p15card->card->ctx, "Data enumeration failed"); return SC_SUCCESS; } for(i=0; i<32; i++) { cinfo = (struct sc_pkcs15_data_info *) objs[i]->data; if(!strcmp("EF_DatiPersonali", objs[i]->label)) break; } if(i>=32) { sc_log(p15card->card->ctx, "Could not find EF_DatiPersonali: " "keeping generic card name"); return SC_SUCCESS; } 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_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, 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; } 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, size_t modulus_len) { int r; sc_path_t path; sc_path_t *private_path = NULL; char pinlabel[16]; int fake_puk_authid, pin_flags; /* 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_len); LOG_TEST_RET(p15card->card->ctx, r, "Could not add public key"); } /* * FIXME: usage should be inferred from the X.509 certificate, and not * from whether the key needs Secure Messaging. */ if (prkey_path) { sc_format_path(prkey_path, &path); private_path = &path; } r = itacns_add_prkey(p15card, cert_id, label, SC_PKCS15_TYPE_PRKEY_RSA, modulus_len, prkey_usage_flags, private_path, sec_env, cert_id, SC_PKCS15_CO_FLAG_PRIVATE); LOG_TEST_RET(p15card->card->ctx, r, "Could not add private key"); /* PIN and PUK */ strlcpy(pinlabel, "PIN ", sizeof(pinlabel)); strlcat(pinlabel, label, sizeof(pinlabel)); /* We are making up ID 0x90+ to link the PIN and the PUK. */ fake_puk_authid = 0x90 + pin_ref; pin_flags = SC_PKCS15_PIN_FLAG_CASE_SENSITIVE | SC_PKCS15_PIN_FLAG_INITIALIZED; r = itacns_add_pin(p15card, pinlabel, sec_env, fake_puk_authid, pin_ref, private_path, pin_flags); LOG_TEST_RET(p15card->card->ctx, r, "Could not add PIN"); strlcpy(pinlabel, "PUK ", sizeof(pinlabel)); strlcat(pinlabel, label, sizeof(pinlabel)); /* * Looking at pkcs15-tcos.c and pkcs15-framework.c, it seems that the * right thing to do here is to define a PUK as a SO PIN. Can anybody * comment on this? */ pin_flags |= SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN | SC_PKCS15_PIN_FLAG_UNBLOCK_DISABLED; r = itacns_add_pin(p15card, pinlabel, fake_puk_authid, 0, pin_ref+1, private_path, pin_flags); LOG_TEST_RET(p15card->card->ctx, r, "Could not add PUK"); return 0; } /* * itacns_check_and_add_keyset() checks for the existence and correctness * of an X.509 certificate. If it is all right, it adds the related keys; * otherwise it aborts. */ static int itacns_check_and_add_keyset(sc_pkcs15_card_t *p15card, const char *label, int sec_env, size_t cert_offset, const char *cert_path, const char *pubkey_path, const char *prkey_path, u8 pin_ref, int *found_certificates) { int r; sc_path_t path; sc_pkcs15_id_t cert_id; int ext_info_ok; int ku = 0, xku = 0; size_t modulus_len = 0; int pubkey_usage_flags = 0, prkey_usage_flags = 0; cert_id.len = 1; cert_id.value[0] = sec_env; *found_certificates = 0; /* Certificate */ if (!cert_path) { sc_log(p15card->card->ctx, "We cannot use keys without a matching certificate"); return SC_ERROR_NOT_SUPPORTED; } sc_format_path(cert_path, &path); r = sc_select_file(p15card->card, &path, NULL); if (r == SC_ERROR_FILE_NOT_FOUND) return 0; if (r != SC_SUCCESS) { sc_log(p15card->card->ctx, "Could not find certificate for %s", label); return r; } /* * Infocamere 1204 (and others?) store a more complex structure. We * are going to read the first bytes to guess its length, and invoke * itacns_add_cert so that it only reads the certificate. */ if (cert_offset) { u8 certlen[3]; memset(certlen, 0, sizeof certlen); r = loadFile(p15card, &path, certlen, sizeof(certlen)); LOG_TEST_RET(p15card->card->ctx, r, "Could not read certificate file"); 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 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, &modulus_len); if (r == SC_ERROR_INVALID_ASN1_OBJECT) return 0; LOG_TEST_RET(p15card->card->ctx, r, "Could not add certificate"); (*found_certificates)++; /* Set usage flags */ if(ext_info_ok) { #ifdef ENABLE_OPENSSL if (ku & KU_DIGITAL_SIGNATURE) { pubkey_usage_flags |= SC_PKCS15_PRKEY_USAGE_VERIFY; prkey_usage_flags |= SC_PKCS15_PRKEY_USAGE_SIGN; } if (ku & KU_NON_REPUDIATION) { pubkey_usage_flags |= SC_PKCS15_PRKEY_USAGE_VERIFY; prkey_usage_flags |= SC_PKCS15_PRKEY_USAGE_NONREPUDIATION; } if (ku & KU_KEY_ENCIPHERMENT || ku & KU_KEY_AGREEMENT || xku & XKU_SSL_CLIENT) { pubkey_usage_flags |= SC_PKCS15_PRKEY_USAGE_WRAP; prkey_usage_flags |= SC_PKCS15_PRKEY_USAGE_UNWRAP; } if (ku & KU_DATA_ENCIPHERMENT || xku & XKU_SMIME) { pubkey_usage_flags |= SC_PKCS15_PRKEY_USAGE_ENCRYPT; prkey_usage_flags |= SC_PKCS15_PRKEY_USAGE_DECRYPT; } #else /* ENABLE_OPENSSL */ sc_log(p15card->card->ctx, "Extended certificate info retrieved without OpenSSL. " "How is this possible?"); return SC_ERROR_INTERNAL; #endif /* ENABLE_OPENSSL */ } else { /* Certificate info not retrieved; fall back onto defaults */ pubkey_usage_flags = SC_PKCS15_PRKEY_USAGE_VERIFY | SC_PKCS15_PRKEY_USAGE_WRAP; prkey_usage_flags = SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_UNWRAP; } r = itacns_add_keyset(p15card, label, sec_env, &cert_id, pubkey_path, prkey_path, pubkey_usage_flags, prkey_usage_flags, pin_ref, modulus_len); LOG_TEST_RET(p15card->card->ctx, r, "Could not add keys for this certificate"); return r; } /* Initialization. */ static int itacns_init(sc_pkcs15_card_t *p15card) { int r; sc_path_t path; int certificate_count = 0; int found_certs; int card_is_cie_v1, cns0_secenv; SC_FUNC_CALLED(p15card->card->ctx, SC_LOG_DEBUG_NORMAL); set_string(&p15card->tokeninfo->label, p15card->card->name); if(p15card->card->drv_data) { unsigned int mask_code, ic_code; char buffer[256]; itacns_drv_data_t *data = (itacns_drv_data_t*) p15card->card->drv_data; mask_code = data->mask_manufacturer_code; if (mask_code >= sizeof(itacns_mask_manufacturers) /sizeof(itacns_mask_manufacturers[0])) mask_code = 0; ic_code = data->ic_manufacturer_code; if (ic_code >= sizeof(iso7816_ic_manufacturers) /sizeof(iso7816_ic_manufacturers[0])) ic_code = 0; snprintf(buffer, sizeof(buffer), "IC: %s; mask: %s", iso7816_ic_manufacturers[ic_code], itacns_mask_manufacturers[mask_code]); set_string(&p15card->tokeninfo->manufacturer_id, buffer); } /* Read and set serial */ { u8 serial[17]; int bytes; sc_format_path(path_serial, &path); bytes = loadFile(p15card, &path, serial, 16); if (bytes < 0) return bytes; if (bytes > 16) return -1; serial[bytes] = '\0'; set_string(&p15card->tokeninfo->serial_number, (char*)serial); } /* Is the card a CIE v1? */ card_is_cie_v1 = (p15card->card->type == SC_CARD_TYPE_ITACNS_CIE_V1) || (p15card->card->type == SC_CARD_TYPE_CARDOS_CIE_V1); cns0_secenv = (card_is_cie_v1 ? 0x31 : 0x01); /* If it's a Siemens CIE v1 card, set algo flags accordingly. */ if (card_is_cie_v1) { int i; for (i = 0; i < p15card->card->algorithm_count; i++) { sc_algorithm_info_t *info = &p15card->card->algorithms[i]; if (info->algorithm != SC_ALGORITHM_RSA) continue; info->flags &= ~(SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_RSA_HASH_NONE); info->flags |= (SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASHES); } } /* Data files */ r = itacns_add_data_files(p15card); LOG_TEST_GOTO_ERR(p15card->card->ctx, r, "Could not add data files"); /*** Certificate and keys. ***/ /* Standard CNS */ r = itacns_check_and_add_keyset(p15card, "CNS0", cns0_secenv, 0, "3F0011001101", "3F003F01", NULL, 0x10, &found_certs); LOG_TEST_GOTO_ERR(p15card->card->ctx, r, "Could not add CNS0"); certificate_count += found_certs; /* Infocamere 1204 */ r = itacns_check_and_add_keyset(p15card, "CNS01", 0x21, 5, "3F002FFF8228", NULL, "3F002FFF0000", 0x10, &found_certs); LOG_TEST_GOTO_ERR(p15card->card->ctx, r, "Could not add CNS01"); certificate_count += found_certs; /* Digital signature */ r = itacns_check_and_add_keyset(p15card, "CNS1", 0x10, 0, "3F0014009010", "3F00140081108010", "3F0014008110", 0x1a, &found_certs); 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_NORMAL, "Warning: no certificates found!"); /* Back to Master File */ sc_format_path("3F00", &path); r = sc_select_file(p15card->card, &path, NULL); LOG_TEST_GOTO_ERR(p15card->card->ctx, r, "Could not select master file again"); 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_card_t *card = p15card->card; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); /* Check 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); } OpenSC-0.26.1/src/libopensc/pkcs15-jpki.c000066400000000000000000000166071474147347300177310ustar00rootroot00000000000000/* * PKCS15 emulation layer for JPKI(Japanese Individual Number Cards). * * Copyright (C) 2016, HAMANO Tsukasa * * 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 "common/compat_strlcat.h" #include "internal.h" #include "pkcs15.h" #include "jpki.h" static int sc_pkcs15emu_jpki_init(sc_pkcs15_card_t * p15card) { sc_card_t *card = p15card->card; struct jpki_private_data *drvdata = JPKI_DRVDATA(card); int i, rc; LOG_FUNC_CALLED(p15card->card->ctx); set_string(&p15card->tokeninfo->label, "JPKI"); set_string(&p15card->tokeninfo->manufacturer_id, "JPKI"); /* set dummy until we found serial number */ set_string(&p15card->tokeninfo->serial_number, "00000000"); /* Select application directory */ if (drvdata->selected != SELECT_JPKI_AP) { rc = jpki_select_ap(card); LOG_TEST_RET(card->ctx, rc, "select AP failed"); drvdata->selected = SELECT_JPKI_AP; } /* add certificates */ for (i = 0; i < 4; i++) { static const char *jpki_cert_names[4] = { "User Authentication Certificate", "Digital Signature Certificate", "User Authentication Certificate CA", "Digital Signature Certificate CA" }; static char const *jpki_cert_paths[4] = { "000A", "0001", "000B", "0002" }; static int jpki_cert_ids[4] = { 1, 2, 3, 4 }; static int jpki_cert_flags[4] = { 0, SC_PKCS15_CO_FLAG_PRIVATE, 0, 0, }; static int jpki_cert_authority[4] = {0, 0, 1, 1}; 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] = jpki_cert_ids[i]; cert_info.id.len = 1; sc_format_path(jpki_cert_paths[i], &cert_info.path); cert_info.path.type = SC_PATH_TYPE_FILE_ID; strlcpy(cert_obj.label, jpki_cert_names[i], sizeof(cert_obj.label)); 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) { sc_pkcs15_card_clear(p15card); LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); } } /* add pins */ for (i = 0; i < 2; i++) { static const char *jpki_pin_names[2] = { "User Authentication PIN", "Digital Signature PIN" }; static const int jpki_pin_min[2] = { 4, 6 }; static const int jpki_pin_max[2] = { 4, 16 }; static const int jpki_pin_ref[2] = { 1, 2 }; static const int jpki_pin_authid[2] = { 1, 2 }; static const int jpki_pin_flags[2] = { SC_PKCS15_PIN_FLAG_INITIALIZED | SC_PKCS15_PIN_FLAG_LOCAL, SC_PKCS15_PIN_FLAG_INITIALIZED | SC_PKCS15_PIN_FLAG_LOCAL }; static const int jpki_pin_max_tries[2] = { JPKI_AUTH_PIN_MAX_TRIES, JPKI_SIGN_PIN_MAX_TRIES }; struct sc_pkcs15_auth_info pin_info; struct sc_pkcs15_object pin_obj; struct sc_pin_cmd_data pin_cmd_data; memset(&pin_info, 0, sizeof (pin_info)); memset(&pin_obj, 0, sizeof (pin_obj)); memset(&pin_cmd_data, 0, sizeof(pin_cmd_data)); pin_info.auth_id.len = 1; pin_info.auth_id.value[0] = jpki_pin_authid[i]; pin_info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; pin_info.attrs.pin.reference = jpki_pin_ref[i]; pin_info.attrs.pin.flags = jpki_pin_flags[i]; pin_info.attrs.pin.type = SC_PKCS15_PIN_TYPE_ASCII_NUMERIC; pin_info.attrs.pin.min_length = jpki_pin_min[i]; pin_info.attrs.pin.stored_length = 0; pin_info.attrs.pin.max_length = jpki_pin_max[i]; pin_info.attrs.pin.pad_char = '\0'; pin_info.max_tries = jpki_pin_max_tries[i]; pin_info.tries_left = -1; pin_info.logged_in = SC_PIN_STATE_UNKNOWN; 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); 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) { sc_pkcs15_card_clear(p15card); LOG_FUNC_RETURN(card->ctx, 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_SIGN, SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_NONREPUDIATION }; static const char *prkey_name[2] = { "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; 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; prkey_info.modulus_length = 2048; 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 = 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) { 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) { if (p15card->card->type != SC_CARD_TYPE_JPKI_BASE) return SC_ERROR_WRONG_CARD; return sc_pkcs15emu_jpki_init(p15card); } OpenSC-0.26.1/src/libopensc/pkcs15-nqApplet.c000066400000000000000000000161021474147347300205460ustar00rootroot00000000000000/* * 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); LOG_TEST_RET(card->ctx, rv, "Failed to add Encr. certificate"); // 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"); 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) { 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); } OpenSC-0.26.1/src/libopensc/pkcs15-oberthur.c000066400000000000000000001076351474147347300206300ustar00rootroot00000000000000/* * PKCS15 emulation layer for Oberthur card. * * Copyright (C) 2010, Viktor Tarasov * Copyright (C) 2005, Andrea Frigido * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "../common/compat_strlcpy.h" #include "pkcs15.h" #include "log.h" #include "asn1.h" #include "internal.h" #ifdef ENABLE_OPENSSL #include #include #include #endif #define OBERTHUR_ATTR_MODIFIABLE 0x0001 #define OBERTHUR_ATTR_TRUSTED 0x0002 #define OBERTHUR_ATTR_LOCAL 0x0004 #define OBERTHUR_ATTR_ENCRYPT 0x0008 #define OBERTHUR_ATTR_DECRYPT 0x0010 #define OBERTHUR_ATTR_SIGN 0x0020 #define OBERTHUR_ATTR_VERIFY 0x0040 #define OBERTHUR_ATTR_RSIGN 0x0080 #define OBERTHUR_ATTR_RVERIFY 0x0100 #define OBERTHUR_ATTR_WRAP 0x0200 #define OBERTHUR_ATTR_UNWRAP 0x0400 #define OBERTHUR_ATTR_DERIVE 0x0800 #define USAGE_PRV_ENC (SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_DECRYPT |\ SC_PKCS15_PRKEY_USAGE_WRAP | SC_PKCS15_PRKEY_USAGE_UNWRAP) #define USAGE_PRV_AUT SC_PKCS15_PRKEY_USAGE_SIGN #define USAGE_PRV_SIGN (SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_NONREPUDIATION) #define USAGE_PUB_ENC (SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_WRAP) #define USAGE_PUB_AUT SC_PKCS15_PRKEY_USAGE_VERIFY #define USAGE_PUB_SIGN (SC_PKCS15_PRKEY_USAGE_VERIFY | SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER) #define PIN_DOMAIN_LABEL "SCM" const unsigned char PinDomainID[3] = {0x53, 0x43, 0x4D}; #define AWP_PIN_DF "3F005011" #define AWP_TOKEN_INFO "3F0050111000" #define AWP_PUK_FILE "3F0050112000" #define AWP_CONTAINERS_MS "3F0050113000" #define AWP_OBJECTS_LIST_PUB "3F0050114000" #define AWP_OBJECTS_LIST_PRV "3F0050115000" #define AWP_OBJECTS_DF_PUB "3F0050119001" #define AWP_OBJECTS_DF_PRV "3F0050119002" #define AWP_BASE_RSA_PRV "3F00501190023000" #define AWP_BASE_RSA_PUB "3F00501190011000" #define AWP_BASE_CERTIFICATE "3F00501190012000" #define BASE_ID_PUB_RSA 0x10 #define BASE_ID_CERT 0x20 #define BASE_ID_PRV_RSA 0x30 #define BASE_ID_PRV_DES 0x40 #define BASE_ID_PUB_DATA 0x50 #define BASE_ID_PRV_DATA 0x60 #define BASE_ID_PUB_DES 0x70 static int sc_pkcs15emu_oberthur_add_prvkey(struct sc_pkcs15_card *, unsigned, unsigned); static int sc_pkcs15emu_oberthur_add_pubkey(struct sc_pkcs15_card *, unsigned, unsigned); 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); 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); static int sc_oberthur_parse_privateinfo (struct sc_pkcs15_card *, unsigned char *, size_t, int); static int sc_awp_parse_df(struct sc_pkcs15_card *, struct sc_pkcs15_df *); static void sc_awp_clear(struct sc_pkcs15_card *); struct crypto_container { unsigned id_pub; unsigned id_prv; unsigned id_cert; }; struct container { char uuid[37]; struct crypto_container exchange; struct crypto_container sign; struct container *next; struct container *prev; }; struct container *Containers = NULL; static struct { const char *name; const char *path; unsigned char *content; size_t len; int (*parser)(struct sc_pkcs15_card *, unsigned char *, size_t, int); int postpone_allowed; } oberthur_infos[] = { /* Never change the following order */ { "Token info", AWP_TOKEN_INFO, NULL, 0, sc_oberthur_parse_tokeninfo, 0}, { "Containers MS", AWP_CONTAINERS_MS, NULL, 0, sc_oberthur_parse_containers, 0}, { "Public objects list", AWP_OBJECTS_LIST_PUB, NULL, 0, sc_oberthur_parse_publicinfo, 0}, { "Private objects list", AWP_OBJECTS_LIST_PRV, NULL, 0, sc_oberthur_parse_privateinfo, 1}, { NULL, NULL, NULL, 0, NULL, 0} }; static unsigned sc_oberthur_decode_usage(unsigned flags) { unsigned ret = 0; if (flags & OBERTHUR_ATTR_ENCRYPT) ret |= SC_PKCS15_PRKEY_USAGE_ENCRYPT; if (flags & OBERTHUR_ATTR_DECRYPT) ret |= SC_PKCS15_PRKEY_USAGE_DECRYPT; if (flags & OBERTHUR_ATTR_SIGN) ret |= SC_PKCS15_PRKEY_USAGE_SIGN; if (flags & OBERTHUR_ATTR_RSIGN) ret |= SC_PKCS15_PRKEY_USAGE_SIGNRECOVER; if (flags & OBERTHUR_ATTR_WRAP) ret |= SC_PKCS15_PRKEY_USAGE_WRAP; if (flags & OBERTHUR_ATTR_UNWRAP) ret |= SC_PKCS15_PRKEY_USAGE_UNWRAP; if (flags & OBERTHUR_ATTR_VERIFY) ret |= SC_PKCS15_PRKEY_USAGE_VERIFY; if (flags & OBERTHUR_ATTR_RVERIFY) ret |= SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER; if (flags & OBERTHUR_ATTR_DERIVE) ret |= SC_PKCS15_PRKEY_USAGE_DERIVE; return ret; } static int sc_oberthur_get_friends (unsigned int id, struct crypto_container *ccont) { struct container *cont; for (cont = Containers; cont; cont = cont->next) { if (cont->exchange.id_pub == id || cont->exchange.id_prv == id || cont->exchange.id_cert == id) { if (ccont) memcpy(ccont, &cont->exchange, sizeof(struct crypto_container)); break; } if (cont->sign.id_pub == id || cont->sign.id_prv == id || cont->sign.id_cert == id) { if (ccont) memcpy(ccont, &cont->sign, sizeof(struct crypto_container)); break; } } return cont ? 0 : SC_ERROR_TEMPLATE_NOT_FOUND; } static int sc_oberthur_get_certificate_authority(sc_context_t *ctx, struct sc_pkcs15_der *der, int *out_authority) { #ifdef ENABLE_OPENSSL X509 *x; BUF_MEM buf_mem; BIO *bio = NULL; BASIC_CONSTRAINTS *bs = NULL; if (!der) return SC_ERROR_INVALID_ARGUMENTS; buf_mem.data = malloc(der->len); if (!buf_mem.data) return SC_ERROR_OUT_OF_MEMORY; memcpy(buf_mem.data, der->value, der->len); buf_mem.max = buf_mem.length = der->len; bio = BIO_new(BIO_s_mem()); 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) { 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) *out_authority = (bs && bs->ca); X509_free(x); return SC_SUCCESS; #else return SC_ERROR_NOT_SUPPORTED; #endif } static int sc_oberthur_read_file(struct sc_pkcs15_card *p15card, const char *in_path, unsigned char **out, size_t *out_len, int verify_pin) { struct sc_context *ctx = p15card->card->ctx; struct sc_card *card = p15card->card; struct sc_file *file = NULL; struct sc_path path; size_t sz; int rv; LOG_FUNC_CALLED(ctx); if (!in_path || !out || !out_len) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Cannot read oberthur file"); sc_log(ctx, "read file '%s'; verify_pin:%i", in_path, verify_pin); *out = NULL; *out_len = 0; sc_format_path(in_path, &path); rv = sc_select_file(card, &path, &file); 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(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 { unsigned int rec; size_t offs = 0; size_t rec_len = file->record_length; for (rec = 1; ; rec++) { 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; } else if (rv < 0) { break; } rec_len = rv; *(*out + offs) = 'R'; *(*out + offs + 1) = rv; offs += rv + 2; } sz = offs; } sc_log(ctx, "read oberthur file result %i", rv); 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, nobjs; 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; ii < nobjs; ii++) { struct sc_pkcs15_auth_info *auth_info = (struct sc_pkcs15_auth_info *) objs[ii]->data; 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); if (auth_info->attrs.pin.reference == (int)acl->key_ref && auth_info->auth_method == (unsigned)acl->method) { pin_obj = objs[ii]; break; } } if (!pin_obj || !pin_obj->content.value) { rv = SC_ERROR_SECURITY_STATUS_NOT_SATISFIED; } else { rv = sc_pkcs15_verify_pin(p15card, pin_obj, pin_obj->content.value, pin_obj->content.len); if (!rv) rv = sc_oberthur_read_file(p15card, in_path, out, out_len, 0); } } sc_file_free(file); if (rv < 0) { free(*out); *out = NULL; *out_len = 0; } *out_len = sz; LOG_FUNC_RETURN(ctx, rv); } static int sc_oberthur_parse_tokeninfo (struct sc_pkcs15_card *p15card, unsigned char *buff, size_t len, int postpone_allowed) { struct sc_context *ctx = p15card->card->ctx; char label[0x21]; unsigned flags; int ii; LOG_FUNC_CALLED(ctx); if (!buff || len < 0x24) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Cannot parse token info"); memset(label, 0, sizeof(label)); memcpy(label, buff, 0x20); ii = 0x20; while (*(label + --ii)==' ' && ii) ; *(label + ii + 1) = '\0'; flags = *(buff + 0x22) * 0x100 + *(buff + 0x23); 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; sc_log(ctx, "label %s", p15card->tokeninfo->label); sc_log(ctx, "manufacturer_id %s", p15card->tokeninfo->manufacturer_id); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int sc_oberthur_parse_containers (struct sc_pkcs15_card *p15card, unsigned char *buff, size_t len, int postpone_allowed) { struct sc_context *ctx = p15card->card->ctx; size_t offs; LOG_FUNC_CALLED(ctx); while (Containers) { struct container *next = Containers->next; free (Containers); Containers = next; } for (offs=0; offs + 2 + 2+2+2 + 2+2+2 + 2+36 <= len;) { struct container *cont; unsigned char *ptr = buff + offs + 2; sc_log(ctx, "parse contaniers offs:%"SC_FORMAT_LEN_SIZE_T"u, len:%"SC_FORMAT_LEN_SIZE_T"u", offs, len); if (*(buff + offs) != 'R') return SC_ERROR_INVALID_DATA; cont = (struct container *)calloc(1, sizeof(struct container)); if (!cont) return SC_ERROR_OUT_OF_MEMORY; cont->exchange.id_pub = *ptr * 0x100 + *(ptr + 1); ptr += 2; cont->exchange.id_prv = *ptr * 0x100 + *(ptr + 1); ptr += 2; cont->exchange.id_cert = *ptr * 0x100 + *(ptr + 1); ptr += 2; cont->sign.id_pub = *ptr * 0x100 + *(ptr + 1); ptr += 2; cont->sign.id_prv = *ptr * 0x100 + *(ptr + 1); ptr += 2; cont->sign.id_cert = *ptr * 0x100 + *(ptr + 1); ptr += 2; memcpy(cont->uuid, ptr + 2, 36); sc_log(ctx, "UUID: %s; 0x%X, 0x%X, 0x%X", cont->uuid, cont->exchange.id_pub, cont->exchange.id_prv, cont->exchange.id_cert); if (!Containers) { Containers = cont; } else { cont->next = Containers; Containers->prev = (void *)cont; Containers = cont; } offs += *(buff + offs + 1) + 2; } LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int sc_oberthur_parse_publicinfo (struct sc_pkcs15_card *p15card, unsigned char *buff, size_t len, int postpone_allowed) { struct sc_context *ctx = p15card->card->ctx; size_t ii; int rv; LOG_FUNC_CALLED(ctx); for (ii=0; ii+5<=len; ii+=5) { unsigned int file_id, size; if(*(buff+ii) != 0xFF) continue; file_id = 0x100 * *(buff+ii + 1) + *(buff+ii + 2); size = 0x100 * *(buff+ii + 3) + *(buff+ii + 4); sc_log(ctx, "add public object(file-id:%04X,size:%X)", file_id, size); switch (*(buff+ii + 1)) { case BASE_ID_PUB_RSA : rv = sc_pkcs15emu_oberthur_add_pubkey(p15card, file_id, size); LOG_TEST_RET(ctx, rv, "Cannot parse public key info"); break; case BASE_ID_CERT : rv = sc_pkcs15emu_oberthur_add_cert(p15card, file_id); LOG_TEST_RET(ctx, rv, "Cannot parse certificate info"); break; case BASE_ID_PUB_DES : break; case BASE_ID_PUB_DATA : rv = sc_pkcs15emu_oberthur_add_data(p15card, file_id, size, 0); LOG_TEST_RET(ctx, rv, "Cannot parse data info"); break; default: LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Public object parse error"); } } LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int sc_oberthur_parse_privateinfo (struct sc_pkcs15_card *p15card, unsigned char *buff, size_t len, int postpone_allowed) { struct sc_context *ctx = p15card->card->ctx; size_t ii; int rv; int no_more_private_keys = 0, no_more_private_data = 0; LOG_FUNC_CALLED(ctx); for (ii=0; ii+5<=len; ii+=5) { unsigned int file_id, size; if(*(buff+ii) != 0xFF) continue; file_id = 0x100 * *(buff+ii + 1) + *(buff+ii + 2); size = 0x100 * *(buff+ii + 3) + *(buff+ii + 4); sc_log(ctx, "add private object (file-id:%04X, size:%X)", file_id, size); switch (*(buff+ii + 1)) { case BASE_ID_PRV_RSA : if (no_more_private_keys) break; rv = sc_pkcs15emu_oberthur_add_prvkey(p15card, file_id, size); if (rv == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED && postpone_allowed) { struct sc_path path; sc_log(ctx, "postpone adding of the private keys"); sc_format_path("5011A5A5", &path); rv = sc_pkcs15_add_df(p15card, SC_PKCS15_PRKDF, &path); LOG_TEST_RET(ctx, rv, "Add PrkDF error"); no_more_private_keys = 1; } LOG_TEST_RET(ctx, rv, "Cannot parse private key info"); break; case BASE_ID_PRV_DES : break; case BASE_ID_PRV_DATA : sc_log(ctx, "*(buff+ii + 1):%X", *(buff+ii + 1)); if (no_more_private_data) break; rv = sc_pkcs15emu_oberthur_add_data(p15card, file_id, size, 1); if (rv == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED && postpone_allowed) { struct sc_path path; sc_log(ctx, "postpone adding of the private data"); sc_format_path("5011A6A6", &path); rv = sc_pkcs15_add_df(p15card, SC_PKCS15_DODF, &path); LOG_TEST_RET(ctx, rv, "Add DODF error"); no_more_private_data = 1; } LOG_TEST_RET(ctx, rv, "Cannot parse private data info"); break; default: LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Private object parse error"); } } LOG_FUNC_RETURN(ctx, SC_SUCCESS); } /* Public key info: * flags:2, * CN(len:2,value:), * ID(len:2,value:(SHA1 value)), * StartDate(Ascii:8) * EndDate(Ascii:8) * ??(0x00:2) */ static int sc_pkcs15emu_oberthur_add_pubkey(struct sc_pkcs15_card *p15card, unsigned int file_id, unsigned int size) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_pubkey_info key_info; struct sc_pkcs15_object key_obj; char ch_tmp[0x100]; unsigned char *info_blob = NULL; size_t len, info_len, offs; unsigned flags; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "public key(file-id:%04X,size:%X)", file_id, size); memset(&key_info, 0, sizeof(key_info)); memset(&key_obj, 0, sizeof(key_obj)); snprintf(ch_tmp, sizeof(ch_tmp), "%s%04X", AWP_OBJECTS_DF_PUB, file_id | 0x100); rv = sc_oberthur_read_file(p15card, ch_tmp, &info_blob, &info_len, 1); LOG_TEST_RET(ctx, rv, "Failed to add public key: read oberthur file error"); /* Flags */ offs = 2; 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) key_obj.flags = SC_PKCS15_CO_FLAG_MODIFIABLE; sc_log(ctx, "Public key key-usage:%04X", key_info.usage); /* Label */ 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 (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); } offs += 2 + len; /* ID */ 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 == 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); sc_format_path(ch_tmp, &key_info.path); key_info.native = 1; key_info.key_reference = file_id & 0xFF; key_info.modulus_length = size; rv = sc_pkcs15emu_add_rsa_pubkey(p15card, &key_obj, &key_info); LOG_FUNC_RETURN(ctx, rv); } /* Certificate info: * flags:2, * Label(len:2,value:), * ID(len:2,value:(SHA1 value)), * Subject in ASN.1(len:2,value:) * Issuer in ASN.1(len:2,value:) * Serial encoded in LV or ASN.1 FIXME */ static int sc_pkcs15emu_oberthur_add_cert(struct sc_pkcs15_card *p15card, unsigned int file_id) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_cert_info cinfo; struct sc_pkcs15_object cobj; unsigned char *info_blob = NULL, *cert_blob = NULL; size_t info_len, cert_len, len, offs; unsigned flags; int rv; char ch_tmp[0x20]; LOG_FUNC_CALLED(ctx); sc_log(ctx, "add certificate(file-id:%04X)", file_id); memset(&cinfo, 0, sizeof(cinfo)); memset(&cobj, 0, sizeof(cobj)); snprintf(ch_tmp, sizeof(ch_tmp), "%s%04X", AWP_OBJECTS_DF_PUB, file_id | 0x100); 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) { 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) { 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 + 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); } offs += 2 + len; /* ID */ 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 + 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); sc_format_path(ch_tmp, &cinfo.path); rv = sc_oberthur_read_file(p15card, ch_tmp, &cert_blob, &cert_len, 1); LOG_TEST_RET(ctx, rv, "Failed to add certificate: read certificate error"); cinfo.value.value = cert_blob; cinfo.value.len = cert_len; 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); LOG_FUNC_RETURN(p15card->card->ctx, rv); } /* Private key info: * flags:2, * CN(len:2,value:), * ID(len:2,value:(SHA1 value)), * StartDate(Ascii:8) * EndDate(Ascii:8) * Subject in ASN.1(len:2,value:) * modulus(value:) * exponent(length:1, value:3) */ static int sc_pkcs15emu_oberthur_add_prvkey(struct sc_pkcs15_card *p15card, unsigned int file_id, unsigned int size) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_prkey_info kinfo; struct sc_pkcs15_object kobj; struct crypto_container ccont; unsigned char *info_blob = NULL; size_t info_len = 0; unsigned flags; size_t offs, len; char ch_tmp[0x100]; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "add private key(file-id:%04X,size:%04X)", file_id, size); memset(&kinfo, 0, sizeof(kinfo)); memset(&kobj, 0, sizeof(kobj)); memset(&ccont, 0, sizeof(ccont)); rv = sc_oberthur_get_friends (file_id, &ccont); LOG_TEST_RET(ctx, rv, "Failed to add private key: get friends error"); if (ccont.id_cert) { struct sc_pkcs15_object *objs[32]; int ii; sc_log(ctx, "friend certificate %04X", ccont.id_cert); rv = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_CERT_X509, objs, 32); LOG_TEST_RET(ctx, rv, "Failed to add private key: get certificates error"); for (ii=0; iidata; struct sc_path path = cert->path; unsigned int id = path.value[path.len - 2] * 0x100 + path.value[path.len - 1]; if (id == ccont.id_cert) { strlcpy(kobj.label, objs[ii]->label, sizeof(kobj.label)); break; } } if (ii == rv) LOG_TEST_RET(ctx, SC_ERROR_INCONSISTENT_PROFILE, "Failed to add private key: friend not found"); } snprintf(ch_tmp, sizeof(ch_tmp), "%s%04X", AWP_OBJECTS_DF_PRV, file_id | 0x100); 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) { 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 + 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; strncpy(kobj.label, (char *)(info_blob + offs + 2), len); } offs += 2 + len; /* ID */ 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) { free(info_blob); LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Failed to add private key: zero length ID"); } 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; /* Ignore Start/End dates */ offs += 16; /* Subject encoded in ASN1 */ 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 + 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) { 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); kinfo.modulus_length = size; kinfo.native = 1; kinfo.key_reference = file_id & 0xFF; kinfo.usage = sc_oberthur_decode_usage(flags); kobj.flags = SC_PKCS15_CO_FLAG_PRIVATE; if (flags & OBERTHUR_ATTR_MODIFIABLE) kobj.flags |= SC_PKCS15_CO_FLAG_MODIFIABLE; kobj.auth_id.len = sizeof(PinDomainID) > sizeof(kobj.auth_id.value) ? sizeof(kobj.auth_id.value) : sizeof(PinDomainID); memcpy(kobj.auth_id.value, PinDomainID, kobj.auth_id.len); sc_log(ctx, "Parsed private key(reference:%i,usage:%X,flags:%X)", kinfo.key_reference, kinfo.usage, kobj.flags); rv = sc_pkcs15emu_add_rsa_prkey(p15card, &kobj, &kinfo); LOG_FUNC_RETURN(ctx, rv); } static int sc_pkcs15emu_oberthur_add_data(struct sc_pkcs15_card *p15card, unsigned int file_id, unsigned int size, int private) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_data_info dinfo; struct sc_pkcs15_object dobj; unsigned flags; unsigned char *info_blob = NULL, *label = NULL, *app = NULL, *oid = NULL; size_t info_len, label_len, app_len, oid_len, offs; char ch_tmp[0x100]; int rv; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); sc_log(ctx, "Add data(file-id:%04X,size:%i,is-private:%i)", file_id, size, private); memset(&dinfo, 0, sizeof(dinfo)); memset(&dobj, 0, sizeof(dobj)); snprintf(ch_tmp, sizeof(ch_tmp), "%s%04X", private ? AWP_OBJECTS_DF_PRV : AWP_OBJECTS_DF_PUB, file_id | 0x100); 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) { 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) { 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 + 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 + 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 (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)) { free(info_blob); LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Failed to add data: invalid 'OID' format"); } oid += 2; oid_len -= 2; } snprintf(ch_tmp, sizeof(ch_tmp), "%s%04X", private ? AWP_OBJECTS_DF_PRV : AWP_OBJECTS_DF_PUB, file_id); sc_format_path(ch_tmp, &dinfo.path); memcpy(dobj.label, label, label_len); memcpy(dinfo.app_label, app, app_len); if (oid_len) sc_asn1_decode_object_id(oid, oid_len, &dinfo.app_oid); if (flags & OBERTHUR_ATTR_MODIFIABLE) dobj.flags |= SC_PKCS15_CO_FLAG_MODIFIABLE; if (private) { dobj.auth_id.len = sizeof(PinDomainID) > sizeof(dobj.auth_id.value) ? sizeof(dobj.auth_id.value) : sizeof(PinDomainID); memcpy(dobj.auth_id.value, PinDomainID, dobj.auth_id.len); dobj.flags |= SC_PKCS15_CO_FLAG_PRIVATE; } rv = sc_pkcs15emu_add_data_object(p15card, &dobj, &dinfo); free(info_blob); LOG_FUNC_RETURN(p15card->card->ctx, rv); } static int sc_pkcs15emu_oberthur_init(struct sc_pkcs15_card * p15card) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_auth_info auth_info; struct sc_pkcs15_object obj; struct sc_card *card = p15card->card; struct sc_path path; int rv, ii, tries_left; char serial[0x10]; unsigned char sopin_reference = 0x04; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_bin_to_hex(card->serialnr.value, card->serialnr.len, serial, sizeof(serial), 0); set_string(&p15card->tokeninfo->serial_number, serial); p15card->ops.parse_df = sc_awp_parse_df; p15card->ops.clear = sc_awp_clear; sc_log(ctx, "Oberthur init: serial %s", p15card->tokeninfo->serial_number); sc_format_path(AWP_PIN_DF, &path); rv = sc_select_file(card, &path, NULL); 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); if (rv && rv != SC_ERROR_PIN_CODE_INCORRECT) { sopin_reference = 0x84; rv = sc_verify(card, SC_AC_CHV, sopin_reference, (unsigned char *)"", 0, &tries_left); } if (rv && rv != SC_ERROR_PIN_CODE_INCORRECT) LOG_TEST_GOTO_ERR(ctx, rv, "Invalid state of SO-PIN"); /* add PIN */ memset(&auth_info, 0, sizeof(auth_info)); memset(&obj, 0, sizeof(obj)); auth_info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; auth_info.auth_method = SC_AC_CHV; auth_info.auth_id.len = 1; auth_info.auth_id.value[0] = 0xFF; auth_info.attrs.pin.min_length = 4; auth_info.attrs.pin.max_length = 64; auth_info.attrs.pin.stored_length = 64; auth_info.attrs.pin.type = SC_PKCS15_PIN_TYPE_ASCII_NUMERIC; auth_info.attrs.pin.reference = sopin_reference; auth_info.attrs.pin.pad_char = 0xFF; auth_info.attrs.pin.flags = SC_PKCS15_PIN_FLAG_CASE_SENSITIVE | SC_PKCS15_PIN_FLAG_INITIALIZED | SC_PKCS15_PIN_FLAG_NEEDS_PADDING | SC_PKCS15_PIN_FLAG_SO_PIN; auth_info.tries_left = tries_left; auth_info.logged_in = SC_PIN_STATE_UNKNOWN; strncpy(obj.label, "SO PIN", SC_PKCS15_MAX_LABEL_SIZE-1); obj.flags = SC_PKCS15_CO_FLAG_MODIFIABLE | SC_PKCS15_CO_FLAG_PRIVATE; 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_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); if (rv == SC_ERROR_PIN_CODE_INCORRECT) { /* add PIN */ memset(&auth_info, 0, sizeof(auth_info)); memset(&obj, 0, sizeof(obj)); auth_info.auth_id.len = sizeof(PinDomainID) > sizeof(auth_info.auth_id.value) ? sizeof(auth_info.auth_id.value) : sizeof(PinDomainID); memcpy(auth_info.auth_id.value, PinDomainID, auth_info.auth_id.len); auth_info.auth_method = SC_AC_CHV; auth_info.attrs.pin.min_length = 4; auth_info.attrs.pin.max_length = 64; auth_info.attrs.pin.stored_length = 64; auth_info.attrs.pin.type = SC_PKCS15_PIN_TYPE_ASCII_NUMERIC; auth_info.attrs.pin.reference = 0x81; auth_info.attrs.pin.pad_char = 0xFF; auth_info.attrs.pin.flags = SC_PKCS15_PIN_FLAG_CASE_SENSITIVE | SC_PKCS15_PIN_FLAG_INITIALIZED | SC_PKCS15_PIN_FLAG_NEEDS_PADDING | SC_PKCS15_PIN_FLAG_LOCAL; auth_info.tries_left = tries_left; strncpy(obj.label, PIN_DOMAIN_LABEL, SC_PKCS15_MAX_LABEL_SIZE-1); obj.flags = SC_PKCS15_CO_FLAG_MODIFIABLE | SC_PKCS15_CO_FLAG_PRIVATE; if (sopin_reference == 0x84) { /* * auth_pin_reset_oberthur_style() in card-oberthur.c * always uses PUK with reference 0x84 for * unblocking of User PIN */ obj.auth_id.len = 1; obj.auth_id.value[0] = 0xFF; } sc_format_path(AWP_PIN_DF, &auth_info.path); auth_info.path.type = SC_PATH_TYPE_PATH; 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_GOTO_ERR(ctx, rv, "Oberthur init failed: cannot add PIN object"); } else if (rv != SC_ERROR_DATA_OBJECT_NOT_FOUND) { 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_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_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); } static int oberthur_detect_card(struct sc_pkcs15_card * p15card) { struct sc_card *card = p15card->card; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (p15card->card->type != SC_CARD_TYPE_OBERTHUR_64K) 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) { int rv; LOG_FUNC_CALLED(p15card->card->ctx); rv = oberthur_detect_card(p15card); if (!rv) rv = sc_pkcs15emu_oberthur_init(p15card); LOG_FUNC_RETURN(p15card->card->ctx, rv); } static int sc_awp_parse_df(struct sc_pkcs15_card *p15card, struct sc_pkcs15_df *df) { struct sc_context *ctx = p15card->card->ctx; unsigned char *buf = NULL; size_t buf_len; int rv; LOG_FUNC_CALLED(ctx); if (df->type != SC_PKCS15_PRKDF && df->type != SC_PKCS15_DODF) LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); if (df->enumerated) 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 private objects info failed"); rv = sc_oberthur_parse_privateinfo(p15card, buf, buf_len, 0); if (buf) free(buf); if (rv == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED) LOG_FUNC_RETURN(ctx, SC_SUCCESS); LOG_TEST_RET(ctx, rv, "Parse DF: private info parse error"); df->enumerated = 1; LOG_FUNC_RETURN(ctx, rv); } static void sc_awp_clear(struct sc_pkcs15_card *p15card) { LOG_FUNC_CALLED(p15card->card->ctx); } OpenSC-0.26.1/src/libopensc/pkcs15-openpgp.c000066400000000000000000000526341474147347300204440ustar00rootroot00000000000000/* * PKCS15 emulation layer for OpenPGP card. * To see how this works, run p15dump on your OpenPGP card. * * 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 #include "common/compat_strlcpy.h" #include "internal.h" #include "pkcs15.h" #include "log.h" #include "card-openpgp.h" static int sc_pkcs15emu_openpgp_add_data(sc_pkcs15_card_t *); #define PGP_USER_PIN_FLAGS (SC_PKCS15_PIN_FLAG_CASE_SENSITIVE \ | SC_PKCS15_PIN_FLAG_INITIALIZED \ | SC_PKCS15_PIN_FLAG_LOCAL) #define PGP_ADMIN_PIN_FLAGS (PGP_USER_PIN_FLAGS \ | SC_PKCS15_PIN_FLAG_UNBLOCK_DISABLED \ | 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; int reference; unsigned int flags; int min_length; 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 */ static const pgp_pin_cfg_t pin_cfg_v1[3] = { { "Encryption PIN", 0x02, PGP_USER_PIN_FLAGS, 6, 1 }, // used for PSO:DEC, INT-AUT, {GET,PUT} DATA { "Signature PIN", 0x01, PGP_USER_PIN_FLAGS, 6, 0 }, // used for PSO:CDS { "Admin PIN", 0x03, PGP_ADMIN_PIN_FLAGS, 8, 2 } }; /* OpenPGP cards v2: * "User PIN (sig)" & "User PIN" are the same PIN, but use different references depending on action */ static const pgp_pin_cfg_t pin_cfg_v2[3] = { { "User PIN", 0x02, PGP_USER_PIN_FLAGS, 6, 0 }, // used for PSO:DEC, INT-AUT, {GET,PUT} DATA { "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 \ | SC_PKCS15_PRKEY_USAGE_NONREPUDIATION) #define PGP_ENC_PRKEY_USAGE (SC_PKCS15_PRKEY_USAGE_DECRYPT \ | SC_PKCS15_PRKEY_USAGE_UNWRAP) #define PGP_AUTH_PRKEY_USAGE (SC_PKCS15_PRKEY_USAGE_NONREPUDIATION) #define PGP_SIG_PUBKEY_USAGE (SC_PKCS15_PRKEY_USAGE_VERIFY \ | SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER) #define PGP_ENC_PUBKEY_USAGE (SC_PKCS15_PRKEY_USAGE_ENCRYPT \ | SC_PKCS15_PRKEY_USAGE_WRAP) #define PGP_AUTH_PUBKEY_USAGE (SC_PKCS15_PRKEY_USAGE_VERIFY) typedef struct _pgp_key_cfg { const char *label; const char *pubkey_path; int prkey_pin; int prkey_usage; 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" }, { 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 } }; // clang-format on /* * This function pretty much follows what find_tlv in the GNUpg * code does. */ static int read_file(sc_card_t *card, const char *path_name, void *buf, size_t len) { sc_path_t path; sc_file_t *file; int r; sc_format_path(path_name, &path); if ((r = sc_select_file(card, &path, &file)) < 0) return r; if (file->size < len) len = file->size; sc_file_free(file); return sc_read_binary(card, 0, (u8 *) buf, len, 0); } static int 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[100]; int r, i; 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 = NULL; LOG_FUNC_CALLED(ctx); set_string(&p15card->tokeninfo->label, "OpenPGP card"); set_string(&p15card->tokeninfo->manufacturer_id, "OpenPGP project"); /* card->serialnr = 2 byte manufacturer_id + 4 byte serial_number */ if (card->serialnr.len > 0) { unsigned short manuf_id = bebytes2ushort(card->serialnr.value); int j; 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++) { if (manuf_id == manuf_map[j].id) { set_string(&p15card->tokeninfo->manufacturer_id, manuf_map[j].name); break; } } } p15card->tokeninfo->flags = SC_PKCS15_TOKEN_PRN_GENERATION | SC_PKCS15_TOKEN_EID_COMPLIANT; /* Extract preferred language */ r = read_file(card, "0065:5f2d", string, sizeof(string)-1); if (r < 0) goto failed; string[r] = '\0'; set_string(&p15card->tokeninfo->preferred_language, string); /* Get CHV status bytes from DO 006E/0073/00C4: * 00: 1 == user consent for signature PIN * (i.e. PIN still valid for next PSO:CDS command) * 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_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 */ for (i = 0; i < 3; i++) { sc_pkcs15_auth_info_t pin_info; sc_pkcs15_object_t 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; pin_info.auth_id.len = 1; pin_info.auth_id.value[0] = pin_cfg[i].reference; pin_info.attrs.pin.reference = pin_cfg[i].reference; pin_info.attrs.pin.flags = pin_cfg[i].flags; pin_info.attrs.pin.type = SC_PKCS15_PIN_TYPE_UTF8; pin_info.attrs.pin.min_length = pin_cfg[i].min_length; pin_info.attrs.pin.stored_length = c4data[1 + pin_cfg[i].do_index]; pin_info.attrs.pin.max_length = c4data[1 + pin_cfg[i].do_index]; pin_info.attrs.pin.pad_char = '\0'; pin_info.tries_left = c4data[4 + pin_cfg[i].do_index]; pin_info.logged_in = SC_PIN_STATE_UNKNOWN; sc_format_path("3F00", &pin_info.path); strlcpy(pin_obj.label, pin_cfg[i].label, sizeof(pin_obj.label)); pin_obj.flags = SC_PKCS15_CO_FLAG_MODIFIABLE | SC_PKCS15_CO_FLAG_PRIVATE; 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) { r = SC_ERROR_INTERNAL; goto failed; } } /* Get private key finger prints from DO 006E/0073/00C5: * 00-19: finger print for SIG key * 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_log(ctx, "finger print bytes have unexpected length (expected 60, got %d)\n", r); r = SC_ERROR_OBJECT_NOT_VALID; goto failed; } 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[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 ((cxdata_len = read_file(card, path_template, cxdata, sizeof(cxdata))) < 1) goto failed; /* check validity using finger prints */ for (j = 19; j >= 0; j--) { if (c5data[20 * i + j] != '\0') break; } /* 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; 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; 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; /* 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[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 ((cxdata_len = read_file(card, path_template, cxdata, sizeof(cxdata))) < 1) goto failed; /* check validity using finger prints */ for (j = 19; j >= 0; j--) { if (c5data[20 * i + j] != '\0') break; } /* 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.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; 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; } } } /* Check if certificate DO 7F21 holds data */ sc_format_path("7F21", &path); r = sc_select_file(card, &path, &file); if (r < 0) goto failed; 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 */ 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, certs[i].label, sizeof(cert_obj.label)); r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info); if (r < 0) { free(buffer); goto failed; } /* only iterate, for OpenPGP >= v3, thus break on < v3 */ if (card->type < SC_CARD_TYPE_OPENPGP_V3) break; } /* 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) { sc_context_t *ctx = p15card->card->ctx; int i, r; LOG_FUNC_CALLED(ctx); /* 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[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)); snprintf(name, 8, "PrivDO%d", i); snprintf(path, 9, "3F00010%d", i); /* 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 ) { sc_log(ctx, "Cannot read DO 010%d or there is no data in it", i); /* Skip */ continue; } sc_format_path(path, &dat_info.path); strlcpy(dat_obj.label, name, sizeof(dat_obj.label)); strlcpy(dat_info.app_label, name, sizeof(dat_info.app_label)); /* Add DATA object to slot protected by PIN2 (PW1 with Ref 0x82) */ dat_obj.flags = SC_PKCS15_CO_FLAG_PRIVATE | SC_PKCS15_CO_FLAG_MODIFIABLE; dat_obj.auth_id.len = 1; if (i == 1 || i == 3) dat_obj.auth_id.value[0] = 2; else dat_obj.auth_id.value[0] = 3; 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, SC_SUCCESS); } static int openpgp_detect_card(sc_pkcs15_card_t *p15card) { 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) { if (openpgp_detect_card(p15card)) return SC_ERROR_WRONG_CARD; return sc_pkcs15emu_openpgp_init(p15card); } OpenSC-0.26.1/src/libopensc/pkcs15-pin.c000066400000000000000000000715541474147347300175640ustar00rootroot00000000000000/* * pkcs15-pin.c: PKCS #15 PIN functions * * Copyright (C) 2001, 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., 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" #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); static const struct sc_asn1_entry c_asn1_com_ao_attr[] = { { "authId", SC_ASN1_PKCS15_ID, SC_ASN1_TAG_OCTET_STRING, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; /* PIN attributes */ static const struct sc_asn1_entry c_asn1_pin_attr[] = { { "pinFlags", SC_ASN1_BIT_FIELD, SC_ASN1_TAG_BIT_STRING, 0, NULL, NULL }, { "pinType", SC_ASN1_ENUMERATED, SC_ASN1_TAG_ENUMERATED, 0, NULL, NULL }, { "minLength", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, { "storedLength", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, { "maxLength", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, SC_ASN1_OPTIONAL, NULL, NULL }, { "pinReference", SC_ASN1_INTEGER, SC_ASN1_CTX | 0, SC_ASN1_OPTIONAL, NULL, NULL }, { "padChar", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_OCTET_STRING, SC_ASN1_OPTIONAL, NULL, NULL }, { "lastPinChange",SC_ASN1_GENERALIZEDTIME, SC_ASN1_TAG_GENERALIZEDTIME, SC_ASN1_OPTIONAL, NULL, NULL }, { "path", SC_ASN1_PATH, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; static const struct sc_asn1_entry c_asn1_type_pin_attr[] = { { "pinAttributes", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; /* Auth Key attributes */ static const struct sc_asn1_entry c_asn1_authkey_attr[] = { { "derivedKey", SC_ASN1_BOOLEAN, SC_ASN1_TAG_BOOLEAN, SC_ASN1_OPTIONAL, NULL, NULL }, { "authKeyId", SC_ASN1_PKCS15_ID, SC_ASN1_TAG_OCTET_STRING, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; static const struct sc_asn1_entry c_asn1_type_authkey_attr[] = { { "authKeyAttributes", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; static const struct sc_asn1_entry c_asn1_auth_type[] = { { "authType", SC_ASN1_CHOICE, 0, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; static const struct sc_asn1_entry c_asn1_auth_type_choice[] = { { "pin", SC_ASN1_PKCS15_OBJECT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { "biometricTemplate", SC_ASN1_PKCS15_OBJECT, SC_ASN1_CTX | 0 | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { "authKey", SC_ASN1_PKCS15_OBJECT, SC_ASN1_CTX | 1 | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; int sc_pkcs15_decode_aodf_entry(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *obj, const u8 ** buf, size_t *buflen) { sc_context_t *ctx = p15card->card->ctx; struct sc_pkcs15_auth_info info; int r; size_t flags_len = sizeof(info.attrs.pin.flags); size_t derived_len = sizeof(info.attrs.authkey.derived); size_t padchar_len = 1; struct sc_asn1_entry asn1_com_ao_attr[2]; struct sc_asn1_entry asn1_pin_attr[10], asn1_type_pin_attr[2]; struct sc_asn1_entry asn1_authkey_attr[3], asn1_type_authkey_attr[2]; struct sc_asn1_entry asn1_auth_type[2]; struct sc_asn1_entry asn1_auth_type_choice[4]; struct sc_asn1_pkcs15_object pin_obj = { obj, asn1_com_ao_attr, NULL, asn1_type_pin_attr }; struct sc_asn1_pkcs15_object authkey_obj = { obj, asn1_com_ao_attr, NULL, asn1_type_authkey_attr }; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_ASN1); sc_copy_asn1_entry(c_asn1_auth_type, asn1_auth_type); sc_copy_asn1_entry(c_asn1_auth_type_choice, asn1_auth_type_choice); sc_copy_asn1_entry(c_asn1_com_ao_attr, asn1_com_ao_attr); sc_copy_asn1_entry(c_asn1_type_pin_attr, asn1_type_pin_attr); sc_copy_asn1_entry(c_asn1_pin_attr, asn1_pin_attr); sc_copy_asn1_entry(c_asn1_type_authkey_attr, asn1_type_authkey_attr); sc_copy_asn1_entry(c_asn1_authkey_attr, asn1_authkey_attr); sc_format_asn1_entry(asn1_auth_type + 0, asn1_auth_type_choice, NULL, 0); sc_format_asn1_entry(asn1_auth_type_choice + 0, &pin_obj, NULL, 0); /* 'pin' */ sc_format_asn1_entry(asn1_auth_type_choice + 2, &authkey_obj, NULL, 0); /* 'authKey' */ /* pinAttributes */ sc_format_asn1_entry(asn1_type_pin_attr + 0, asn1_pin_attr, NULL, 0); sc_format_asn1_entry(asn1_pin_attr + 0, &info.attrs.pin.flags, &flags_len, 0); sc_format_asn1_entry(asn1_pin_attr + 1, &info.attrs.pin.type, NULL, 0); sc_format_asn1_entry(asn1_pin_attr + 2, &info.attrs.pin.min_length, NULL, 0); sc_format_asn1_entry(asn1_pin_attr + 3, &info.attrs.pin.stored_length, NULL, 0); sc_format_asn1_entry(asn1_pin_attr + 4, &info.attrs.pin.max_length, NULL, 0); sc_format_asn1_entry(asn1_pin_attr + 5, &info.attrs.pin.reference, NULL, 0); sc_format_asn1_entry(asn1_pin_attr + 6, &info.attrs.pin.pad_char, &padchar_len, 0); /* authKeyAttributes */ sc_format_asn1_entry(asn1_type_authkey_attr + 0, asn1_authkey_attr, NULL, 0); sc_format_asn1_entry(asn1_authkey_attr + 0, &info.attrs.authkey.derived, &derived_len, 0); sc_format_asn1_entry(asn1_authkey_attr + 1, &info.attrs.authkey.skey_id, NULL, 0); /* We don't support lastPinChange yet. */ sc_format_asn1_entry(asn1_pin_attr + 8, &info.path, NULL, 0); sc_format_asn1_entry(asn1_com_ao_attr + 0, &info.auth_id, NULL, 0); /* Fill in defaults */ memset(&info, 0, sizeof(info)); info.tries_left = -1; info.logged_in = SC_PIN_STATE_UNKNOWN; r = sc_asn1_decode(ctx, asn1_auth_type, *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_auth_type_choice[0].flags & SC_ASN1_PRESENT) { sc_log(ctx, "AuthType: PIN"); obj->type = SC_PKCS15_TYPE_AUTH_PIN; info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; info.auth_method = SC_AC_CHV; if (info.attrs.pin.max_length == 0) { if (p15card->card->max_pin_len != 0) info.attrs.pin.max_length = p15card->card->max_pin_len; else if (info.attrs.pin.stored_length != 0) info.attrs.pin.max_length = info.attrs.pin.type != SC_PKCS15_PIN_TYPE_BCD ? info.attrs.pin.stored_length : 2 * info.attrs.pin.stored_length; else info.attrs.pin.max_length = 8; /* shouldn't happen */ } /* OpenSC 0.11.4 and older encoded "pinReference" as a negative value. Fixed in 0.11.5 we need to add a hack, so old cards continue to work. The same invalid encoding has some models of the proprietary PKCS#15 cards. */ if (info.attrs.pin.reference < 0) info.attrs.pin.reference += 256; if (info.attrs.pin.flags & SC_PKCS15_PIN_FLAG_LOCAL) { /* In OpenSC pkcs#15 framework 'path' is mandatory for the 'Local' PINs. * If 'path' do not present in PinAttributes, derive it from the PKCS#15 context. */ if (!info.path.len) { /* 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 && 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) { 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"); obj->type = SC_PKCS15_TYPE_AUTH_AUTHKEY; info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_AUTH_KEY; info.auth_method = SC_AC_AUT; if (!(asn1_authkey_attr[0].flags & SC_ASN1_PRESENT)) info.attrs.authkey.derived = 1; } else { LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "unknown authentication type"); } obj->data = malloc(sizeof(info)); if (obj->data == NULL) 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); } int sc_pkcs15_encode_aodf_entry(sc_context_t *ctx, const struct sc_pkcs15_object *obj, u8 **buf, size_t *buflen) { struct sc_asn1_entry asn1_com_ao_attr[2], asn1_pin_attr[10], asn1_type_pin_attr[2]; struct sc_asn1_entry asn1_auth_type[2]; struct sc_asn1_entry asn1_auth_type_choice[4]; struct sc_pkcs15_auth_info *info = (struct sc_pkcs15_auth_info *) obj->data; struct sc_asn1_pkcs15_object pin_obj = { (struct sc_pkcs15_object *) obj, asn1_com_ao_attr, NULL, asn1_type_pin_attr }; int r; size_t flags_len; size_t padchar_len = 1; if (info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return SC_ERROR_NOT_SUPPORTED; sc_copy_asn1_entry(c_asn1_auth_type, asn1_auth_type); sc_copy_asn1_entry(c_asn1_auth_type_choice, asn1_auth_type_choice); sc_copy_asn1_entry(c_asn1_type_pin_attr, asn1_type_pin_attr); sc_copy_asn1_entry(c_asn1_pin_attr, asn1_pin_attr); sc_copy_asn1_entry(c_asn1_com_ao_attr, asn1_com_ao_attr); sc_format_asn1_entry(asn1_auth_type + 0, asn1_auth_type_choice, NULL, 1); sc_format_asn1_entry(asn1_auth_type_choice + 0, &pin_obj, NULL, 1); sc_format_asn1_entry(asn1_type_pin_attr + 0, asn1_pin_attr, NULL, 1); flags_len = sizeof(info->attrs.pin.flags); sc_format_asn1_entry(asn1_pin_attr + 0, &info->attrs.pin.flags, &flags_len, 1); sc_format_asn1_entry(asn1_pin_attr + 1, &info->attrs.pin.type, NULL, 1); sc_format_asn1_entry(asn1_pin_attr + 2, &info->attrs.pin.min_length, NULL, 1); sc_format_asn1_entry(asn1_pin_attr + 3, &info->attrs.pin.stored_length, NULL, 1); if (info->attrs.pin.max_length > 0) sc_format_asn1_entry(asn1_pin_attr + 4, &info->attrs.pin.max_length, NULL, 1); if (info->attrs.pin.reference >= 0) sc_format_asn1_entry(asn1_pin_attr + 5, &info->attrs.pin.reference, NULL, 1); /* FIXME: check if pad_char present */ sc_format_asn1_entry(asn1_pin_attr + 6, &info->attrs.pin.pad_char, &padchar_len, 1); sc_format_asn1_entry(asn1_pin_attr + 8, &info->path, NULL, info->path.len ? 1 : 0); sc_format_asn1_entry(asn1_com_ao_attr + 0, &info->auth_id, NULL, 1); r = sc_asn1_encode(ctx, asn1_auth_type, buf, buflen); return r; } static int _validate_pin(struct sc_pkcs15_card *p15card, struct sc_pkcs15_auth_info *auth_info, size_t pinlen) { size_t max_length; if (p15card == NULL) { return SC_ERROR_INVALID_ARGUMENTS; } /* Ignore validation of the non-PIN authentication objects */ if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return SC_SUCCESS; /* prevent buffer overflow from hostile card */ if (auth_info->attrs.pin.stored_length > SC_MAX_PIN_SIZE) return SC_ERROR_BUFFER_TOO_SMALL; /* if we use pinpad, no more checks are needed */ if ((p15card->card->reader->capabilities & SC_READER_CAP_PIN_PAD || p15card->card->caps & SC_CARD_CAP_PROTECTED_AUTHENTICATION_PATH) && !pinlen) return SC_SUCCESS; /* If pin is given, make sure it is within limits */ max_length = auth_info->attrs.pin.max_length != 0 ? auth_info->attrs.pin.max_length : SC_MAX_PIN_SIZE; if (pinlen > max_length || pinlen < auth_info->attrs.pin.min_length) return SC_ERROR_INVALID_PIN_LENGTH; return SC_SUCCESS; } /* * Verify a PIN. * * If the code given to us has zero length, this means we * should ask the card reader to obtain the PIN from the * reader's PIN pad */ int sc_pkcs15_verify_pin(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *pin_obj, const unsigned char *pincode, size_t pinlen) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_auth_info *auth_info; int r; LOG_FUNC_CALLED(ctx); 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) LOG_FUNC_RETURN(ctx, r); r = _sc_pkcs15_verify_pin(p15card, pin_obj, pincode, pinlen); if (r == SC_SUCCESS) sc_pkcs15_pincache_add(p15card, pin_obj, pincode, pinlen); LOG_FUNC_RETURN(ctx, r); } int _sc_pkcs15_verify_pin(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *pin_obj, const unsigned char *pincode, size_t pinlen) { return sc_pkcs15_verify_pin_with_session_pin(p15card, pin_obj, pincode, pinlen, NULL, NULL); } /* * Verify a PIN and generate a session PIN * * If the code given to us has zero length, this means we * should ask the card reader to obtain the PIN from the * reader's PIN pad */ int sc_pkcs15_verify_pin_with_session_pin(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *pin_obj, const unsigned char *pincode, size_t pinlen, const unsigned char *sessionpin, size_t *sessionpinlen) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_auth_info *auth_info = (struct sc_pkcs15_auth_info *)pin_obj->data; int r; sc_card_t *card; struct sc_pin_cmd_data data; LOG_FUNC_CALLED(ctx); sc_log(ctx, "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) { sc_notify_id(card->ctx, &card->reader->atr, p15card, NOTIFY_PIN_BAD); LOG_TEST_RET(ctx, SC_ERROR_INVALID_PIN_LENGTH, "Invalid PIN size"); } /* Initialize arguments */ memset(&data, 0, sizeof(data)); data.pin_type = auth_info->auth_method; if (auth_info->auth_type == SC_PKCS15_PIN_AUTH_TYPE_PIN) { data.pin_reference = auth_info->attrs.pin.reference; 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 = auth_info->attrs.pin.pad_char; data.pin1.data = pincode; data.pin1.len = pinlen; if (auth_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_NEEDS_PADDING) data.flags |= SC_PIN_CMD_NEED_PADDING; switch (auth_info->attrs.pin.type) { case SC_PKCS15_PIN_TYPE_BCD: data.pin1.encoding = SC_PIN_ENCODING_BCD; break; case SC_PKCS15_PIN_TYPE_ASCII_NUMERIC: data.pin1.encoding = SC_PIN_ENCODING_ASCII; break; default: /* assume/hope the card driver knows how to encode the pin */ data.pin1.encoding = 0; } } else if (auth_info->auth_type == SC_PKCS15_PIN_AUTH_TYPE_AUTH_KEY) { struct sc_pkcs15_object *skey_obj = NULL; struct sc_pkcs15_id *skey_id = &auth_info->attrs.authkey.skey_id; struct sc_pkcs15_skey_info *skey_info = NULL; r = sc_pkcs15_find_skey_by_id(p15card, skey_id, &skey_obj); if (r) { sc_log(ctx, "cannot find secret key with id:%s", sc_pkcs15_print_id(skey_id)); LOG_FUNC_RETURN(ctx, r); } skey_info = (struct sc_pkcs15_skey_info *)skey_obj->data; sc_log(ctx, "found secret key '%s'", skey_obj->label); 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)) { 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"; else data.pin1.prompt = "Please enter PIN"; } if (card->caps & SC_CARD_CAP_SESSION_PIN && sessionpin && sessionpinlen) { /* session pin is requested and supported with standard verification*/ data.cmd = SC_PIN_CMD_GET_SESSION_PIN; memcpy(&data.pin2, &data.pin1, sizeof (data.pin1)); data.pin2.data = sessionpin; data.pin2.len = *sessionpinlen; } else { /* perform a standard verify */ data.cmd = SC_PIN_CMD_VERIFY; if (sessionpinlen) *sessionpinlen = 0; } r = sc_lock(card); LOG_TEST_RET(ctx, r, "sc_lock() failed"); /* the path in the pin object is optional */ if ((auth_info->path.len > 0) || ((auth_info->path.aid.len > 0))) { r = sc_select_file(card, &auth_info->path, NULL); if (r) goto out; } r = sc_pin_cmd(card, &data, &auth_info->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 && sessionpinlen) { *sessionpinlen = data.pin2.len; } } else { 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); } /* * Change a PIN. */ int sc_pkcs15_change_pin(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *pin_obj, const u8 *oldpin, size_t oldpinlen, const u8 *newpin, size_t newpinlen) { struct sc_context *ctx = p15card->card->ctx; struct sc_pin_cmd_data data; struct sc_pkcs15_auth_info *auth_info = (struct sc_pkcs15_auth_info *)pin_obj->data; struct sc_card *card; int r; LOG_FUNC_CALLED(ctx); if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); /* make sure the pins are in valid range */ r = _validate_pin(p15card, auth_info, oldpinlen); LOG_TEST_RET(ctx, r, "Old PIN value do not conform PIN policy"); r = _validate_pin(p15card, auth_info, newpinlen); LOG_TEST_RET(ctx, r, "New PIN value do not conform PIN policy"); card = p15card->card; r = sc_lock(card); LOG_TEST_RET(ctx, r, "sc_lock() failed"); /* the path in the pin object is optional */ if ((auth_info->path.len > 0) || ((auth_info->path.aid.len > 0))) { r = sc_select_file(card, &auth_info->path, NULL); if (r) goto out; } /* set pin_cmd data */ memset(&data, 0, sizeof(data)); data.cmd = SC_PIN_CMD_CHANGE; data.pin_type = SC_AC_CHV; data.pin_reference = auth_info->attrs.pin.reference; data.pin1.data = oldpin; data.pin1.len = oldpinlen; 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.pin2.data = newpin; data.pin2.len = newpinlen; 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; switch (auth_info->attrs.pin.type) { case SC_PKCS15_PIN_TYPE_BCD: data.pin1.encoding = SC_PIN_ENCODING_BCD; data.pin2.encoding = SC_PIN_ENCODING_BCD; break; case SC_PKCS15_PIN_TYPE_ASCII_NUMERIC: data.pin1.encoding = SC_PIN_ENCODING_ASCII; data.pin2.encoding = SC_PIN_ENCODING_ASCII; break; } if((!oldpin || !newpin) && (p15card->card->reader->capabilities & SC_READER_CAP_PIN_PAD || p15card->card->caps & SC_CARD_CAP_PROTECTED_AUTHENTICATION_PATH)) { 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"; data.pin2.prompt = "Please enter new SO PIN"; } else { data.pin1.prompt = "Please enter PIN"; data.pin2.prompt = "Please enter new PIN"; } } r = sc_pin_cmd(card, &data, &auth_info->tries_left); if (r == SC_SUCCESS) sc_pkcs15_pincache_add(p15card, pin_obj, newpin, newpinlen); out: sc_unlock(card); return r; } /* * Unblock a PIN. */ int sc_pkcs15_unblock_pin(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *pin_obj, const u8 *puk, size_t puklen, const u8 *newpin, size_t newpinlen) { struct sc_context *ctx = p15card->card->ctx; 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; LOG_FUNC_CALLED(ctx); if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); /* make sure the pins are in valid range */ r = _validate_pin(p15card, auth_info, newpinlen); LOG_TEST_RET(ctx, r, "New PIN value do not conform PIN policy"); /* get pin_info object of the puk (this is a little bit complicated * as we don't have the id of the puk (at least now)) * note: for compatibility reasons we give no error if no puk object * is found */ /* first step: try to get the pkcs15 object of the puk */ r = sc_pkcs15_find_pin_by_auth_id(p15card, &pin_obj->auth_id, &puk_obj); 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) { sc_log(ctx, "Unable to get puk object, using pin object instead!"); puk_info = auth_info; } /* make sure the puk is in valid range */ 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"); /* the path in the pin object is optional */ if ((auth_info->path.len > 0) || ((auth_info->path.aid.len > 0))) { r = sc_select_file(card, &auth_info->path, NULL); if (r) goto out; } /* set pin_cmd data */ memset(&data, 0, sizeof(data)); 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 = 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 = 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; switch (auth_info->attrs.pin.type) { case SC_PKCS15_PIN_TYPE_BCD: data.pin1.encoding = SC_PIN_ENCODING_BCD; break; case SC_PKCS15_PIN_TYPE_ASCII_NUMERIC: data.pin1.encoding = SC_PIN_ENCODING_ASCII; break; } switch (puk_info->attrs.pin.type) { case SC_PKCS15_PIN_TYPE_BCD: data.pin2.encoding = SC_PIN_ENCODING_BCD; break; case SC_PKCS15_PIN_TYPE_ASCII_NUMERIC: data.pin2.encoding = SC_PIN_ENCODING_ASCII; break; } if((p15card->card->reader->capabilities & SC_READER_CAP_PIN_PAD || p15card->card->caps & SC_CARD_CAP_PROTECTED_AUTHENTICATION_PATH)) { data.flags |= SC_PIN_CMD_USE_PINPAD; if (auth_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN) { data.pin1.prompt = "Please enter PUK"; data.pin2.prompt = "Please enter new SO PIN"; } else { data.pin1.prompt = "Please enter PUK"; data.pin2.prompt = "Please enter new PIN"; } } r = sc_pin_cmd(card, &data, &auth_info->tries_left); if (r == SC_SUCCESS) sc_pkcs15_pincache_add(p15card, pin_obj, newpin, newpinlen); out: sc_unlock(card); LOG_FUNC_RETURN(ctx, r); } int sc_pkcs15_get_pin_info(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *pin_obj) { int r; struct sc_pin_cmd_data data; struct sc_card *card = p15card->card; struct sc_context *ctx = card->ctx; struct sc_pkcs15_auth_info *pin_info = (struct sc_pkcs15_auth_info *) pin_obj->data; LOG_FUNC_CALLED(ctx); r = sc_lock(card); if (r != SC_SUCCESS) return r; if (pin_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) { r = SC_ERROR_INVALID_DATA; 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 = pin_info->auth_method; data.pin_reference = pin_info->attrs.pin.reference; r = sc_pin_cmd(card, &data, NULL); 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; } out: sc_unlock(card); LOG_FUNC_RETURN(ctx, r); } void sc_pkcs15_free_auth_info(sc_pkcs15_auth_info_t *auth_info) { free(auth_info); } /* Add a PIN to the PIN cache related to the card. Some operations can trigger re-authentication later. */ void sc_pkcs15_pincache_add(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *pin_obj, const u8 *pin, 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_object *obj = NULL; int r; LOG_FUNC_CALLED(ctx); if (!pin || !pinlen) { sc_log(ctx, "No cache for zero length PIN"); return; } else if (!p15card->opts.use_pin_cache) { sc_log(ctx, "PIN caching not enabled"); return; } else if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) { sc_log(ctx, "only 'PIN' auth. object can be cached"); return; } /* If the PIN protects an object with user consent, don't cache it */ obj = p15card->obj_list; while (obj != NULL) { /* Compare 'sc_pkcs15_object.auth_id' with 'sc_pkcs15_pin_info.auth_id'. * In accordance with PKCS#15 "6.1.8 CommonObjectAttributes" and * "6.1.16 CommonAuthenticationObjectAttributes" with the exception that * "CommonObjectAttributes.accessControlRules" are not taken into account. */ 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; } } } obj = obj->next; } r = sc_pkcs15_allocate_object_content(ctx, pin_obj, pin, pinlen); if (r != SC_SUCCESS) { sc_log(ctx, "Failed to allocate object content"); return; } pin_obj->usage_counter = 0; sc_log(ctx, "PIN(%s) cached", pin_obj->label); } /* Validate the PIN code associated with an object */ int sc_pkcs15_pincache_revalidate(struct sc_pkcs15_card *p15card, const sc_pkcs15_object_t *obj) { struct sc_context *ctx = p15card->card->ctx; sc_pkcs15_object_t *pin_obj; int r; LOG_FUNC_CALLED(ctx); if (!p15card->opts.use_pin_cache) return SC_ERROR_SECURITY_STATUS_NOT_SATISFIED; /* Apps that do not support CK_ALWAYS_AUTHENTICATE * may need pin_cache_ignore_user_consent = 1 */ if (!p15card->opts.pin_cache_ignore_user_consent) { if (obj->user_consent) return SC_ERROR_SECURITY_STATUS_NOT_SATISFIED; } if ((p15card->card->reader->capabilities & SC_READER_CAP_PIN_PAD || p15card->card->caps & SC_CARD_CAP_PROTECTED_AUTHENTICATION_PATH)) return SC_ERROR_SECURITY_STATUS_NOT_SATISFIED; r = sc_pkcs15_find_pin_by_auth_id(p15card, &obj->auth_id, &pin_obj); if (r != SC_SUCCESS) { sc_log(ctx, "Could not find pin object for auth_id %s", sc_pkcs15_print_id(&obj->auth_id)); return SC_ERROR_SECURITY_STATUS_NOT_SATISFIED; } if (pin_obj->usage_counter >= p15card->opts.pin_cache_counter) { sc_pkcs15_free_object_content(pin_obj); return SC_ERROR_SECURITY_STATUS_NOT_SATISFIED; } if (!pin_obj->content.value || !pin_obj->content.len) return SC_ERROR_SECURITY_STATUS_NOT_SATISFIED; pin_obj->usage_counter++; r = _sc_pkcs15_verify_pin(p15card, pin_obj, pin_obj->content.value, pin_obj->content.len); if (r != SC_SUCCESS) { /* Ensure that wrong PIN isn't used again */ sc_pkcs15_free_object_content(pin_obj); sc_log(ctx, "Verify PIN error %i", r); return SC_ERROR_SECURITY_STATUS_NOT_SATISFIED; } LOG_FUNC_RETURN(ctx, SC_SUCCESS); } void sc_pkcs15_pincache_clear(struct sc_pkcs15_card *p15card) { struct sc_pkcs15_object *objs[32]; int i, r; 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]); } OpenSC-0.26.1/src/libopensc/pkcs15-piv.c000066400000000000000000001314321474147347300175640ustar00rootroot00000000000000/* * 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) 2020 Douglas E. Engert * 2004, Nils Larsch * Copyright (C) 2006, Identity Alliance, * Thomas Harning * Copyright (C) 2007, EMC, Russell Larner * * 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" #include "cardctl.h" #include "asn1.h" #include "pkcs15.h" #define MANU_ID "piv_II " typedef struct objdata_st { const char *id; const char *label; const char *aoid; const char *auth_id; const char *path; int obj_flags; } objdata; typedef struct cdata_st { const char *id; const char *label; const char *path; int authority; 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 unsigned char pad_char; int obj_flags; int cardmod; /* only use with cardmod minidriver */ } pindata; typedef struct pubdata_st { const char *id; const char *label; int usage_rsa; int usage_ec; const char *path; int ref; const char *auth_id; int obj_flags; const char *getenvname; } pubdata; typedef struct prdata_st { const char *id; const char *label; int usage_rsa; int usage_ec; const char *path; int ref; const char *auth_id; int obj_flags; int user_consent; } prdata; typedef struct common_key_info_st { int cert_found; int pubkey_found; int pubkey_from_file; 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; struct sc_pkcs15_pubkey *pubkey_from_cert; int not_present; } common_key_info; /* * The PIV applet has no serial number, and so the either the FASC-N * 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 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. */ static int piv_get_guid(struct sc_pkcs15_card *p15card, const struct sc_pkcs15_object *obj, unsigned char *out, size_t *out_size) { struct sc_serial_number serialnr; struct sc_pkcs15_id id; unsigned char guid_bin[SC_PKCS15_MAX_ID_SIZE + SC_MAX_SERIALNR]; size_t bin_size, offs, tlen, i; int r; unsigned char fbit, fbits, fbyte, fbyte2, fnibble; unsigned char *f5p, *f8p; if (!p15card || !obj || !out || *out_size < 3) return SC_ERROR_INCORRECT_PARAMETERS; r = sc_pkcs15_get_object_id(obj, &id); if (r) return r; 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); if (id.len == 1 && serialnr.len == 25) { /* It is from a FASCN, and we need to shorten it but keep * 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 separated by 0x0d "=" * Ends with End Sentinel 0x0f "?" * 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. */ f5p = serialnr.value; f8p = guid_bin; fbyte = 0; fbyte2 = 0; fnibble = 0; fbits = 0; for (i = 0; i < 25*8; i++) { if (i%8 == 0) { fbyte=*f5p++; } fbit = (fbyte & 0x80) ? 1:0; fbyte <<= 1; fbits = (fbits >> 1) + (fbit << 4); /* reversed with parity */ if ((i - 4)%5 == 0) { fbits = fbits & 0x0f; /* drop parity */ if (fbits <= 9) { /* only save digits, drop control codes */ fbyte2 = (fbyte2 << 4) | fbits; if (fnibble) { *f8p = fbyte2; f8p++; fbyte2 = 0; fnibble = 0; } else fnibble = 1; } fbits = 0; } } /* overwrite two insignificant digits in middle with id */ 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 * first byte with id.value, as this preserves most * 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 */ memcpy(guid_bin, serialnr.value, serialnr.len); memcpy(guid_bin + serialnr.len, id.value, id.len); tlen = id.len + serialnr.len; } /* reserve one byte for the 'C' line ending */ bin_size = (*out_size - 1)/2; if (bin_size > tlen) bin_size = tlen; offs = tlen - bin_size; for (i=0; icard; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); 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; } 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", "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}, {"03", "Unsigned Card Holder Unique Identifier", "2.16.840.1.101.3.7.2.48.2", NULL, "3010", 0}, {"04", "X.509 Certificate for PIV Authentication", "2.16.840.1.101.3.7.2.1.1", NULL, "0101", 0}, {"05", "Cardholder Fingerprints", "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", "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", "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}, {"11", "Security Object", "2.16.840.1.101.3.7.2.144.0", NULL, "9000", 0}, {"12", "Discovery Object", "2.16.840.1.101.3.7.2.96.80", NULL, "6050", 0}, {"13", "Key History Object", "2.16.840.1.101.3.7.2.96.96", NULL, "6060", 0}, {"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", "2.16.840.1.101.3.7.2.16.1", NULL, "1001", 0}, {"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", "2.16.840.1.101.3.7.2.16.3", NULL, "1003", 0}, {"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", "2.16.840.1.101.3.7.2.16.5", NULL, "1005", 0}, {"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", "2.16.840.1.101.3.7.2.16.7", NULL, "1007", 0}, {"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", "2.16.840.1.101.3.7.2.16.9", NULL, "1009", 0}, {"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", "2.16.840.1.101.3.7.2.16.11", NULL, "100B", 0}, {"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", "2.16.840.1.101.3.7.2.16.13", NULL, "100D", 0}, {"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", "2.16.840.1.101.3.7.2.16.15", NULL, "100F", 0}, {"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", "2.16.840.1.101.3.7.2.16.17", NULL, "1011", 0}, {"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", "2.16.840.1.101.3.7.2.16.19", NULL, "1013", 0}, {"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} }; // 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 */ /* 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", "PIN", "", 0x80, /* label, flag and ref will change if using global pin */ 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, -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_SO_PIN | SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN, -1, 0xFF, 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, SC_PKCS15_PIN_FLAG_NEEDS_PADDING | SC_PKCS15_PIN_FLAG_INITIALIZED | 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. * Key usages bits taken from pkcs15v1_1 Table 2 * RSA and EC have different sets of usage */ // 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", /*RSA*/SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_VERIFY | SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER | SC_PKCS15_PRKEY_USAGE_NONREPUDIATION, /*EC*/SC_PKCS15_PRKEY_USAGE_VERIFY | SC_PKCS15_PRKEY_USAGE_NONREPUDIATION, "9C06", 0x9C, NULL, 0, "PIV_9C_KEY"}, { "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", /*RSA*/SC_PKCS15_PRKEY_USAGE_VERIFY | 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}, { "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}, { "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}, { "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}, { "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}, { "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}, { "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}, { "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}, { "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}, { "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}, { "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}, { "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}, { "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}, { "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}, { "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}, { "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}, { "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}, { "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}, { "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}, { "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} }; // clang-format on /* * 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 */ // 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", /*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 | SC_PKCS15_PRKEY_USAGE_NONREPUDIATION, "", 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", /*RSA*/SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_SIGNRECOVER, /*EC*/SC_PKCS15_PRKEY_USAGE_SIGN, "", 0x9E, NULL, 0, 0}, /* no PIN needed, works with wireless */ { "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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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]; int follows_nist_fascn = 0; char *token_name = NULL; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); memset(&serial, 0, sizeof(serial)); /* could read this off card if needed */ /* CSP does not like a - in the name */ set_string(&p15card->tokeninfo->label, "PIV_II"); set_string(&p15card->tokeninfo->manufacturer_id, MANU_ID); /* * 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 */ 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); } /* 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_log(card->ctx, "PIV-II adding objects..."); /* set other objects */ 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); /* See if the object can not be present on the card */ 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) 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); if (r < 0) 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 initializing, * and the pubkey is in a file,) then add pubkey and privkey * If no cert and no pubkey, skip adding them. */ /* set certs */ 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; ckis[i].pubkey_from_file = 0; ckis[i].pubkey_len = 0; ckis[i].pubkey_from_cert = NULL; ckis[i].cert_keyUsage = 0; ckis[i].cert_keyUsage_present = 0; ckis[i].pub_usage = 0; ckis[i].priv_usage = 0; 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; /* See if the cert might be present or not. */ r = sc_card_ctl(card, SC_CARDCTL_PIV_OBJECT_PRESENT, &cert_info.path); if (r == 1) { sc_log(card->ctx, "Cert can not be present,i=%d", i); continue; } 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_log(card->ctx, "No cert found,i=%d", i); continue; } ckis[i].cert_found = 1; /* 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; 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, 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 */ if (follows_nist_fascn == 0) { struct sc_object_id keyUsage_oid={{2,5,29,15,-1}}; int r = 0; r = sc_pkcs15_get_bitstring_extension(card->ctx, cert_out, &keyUsage_oid, &ckis[i].cert_keyUsage, NULL); if ( r >= 0) ckis[i].cert_keyUsage_present = 1; /* TODO if no key usage, we could set all uses */ } ckis[i].key_alg = cert_out->key->algorithm; switch (cert_out->key->algorithm) { case SC_ALGORITHM_RSA: /* save pubkey_len for pub and priv */ ckis[i].pubkey_len = cert_out->key->u.rsa.modulus.len * 8; /* See RFC 5280 and PKCS#11 V2.40 */ if (ckis[i].cert_keyUsage_present) { if (ckis[i].cert_keyUsage & SC_X509_DIGITAL_SIGNATURE) { ckis[i].pub_usage |= SC_PKCS15_PRKEY_USAGE_ENCRYPT /* extra*/ |SC_PKCS15_PRKEY_USAGE_WRAP |SC_PKCS15_PRKEY_USAGE_VERIFY |SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER; ckis[i].priv_usage |= SC_PKCS15_PRKEY_USAGE_DECRYPT /*extra */ |SC_PKCS15_PRKEY_USAGE_UNWRAP |SC_PKCS15_PRKEY_USAGE_SIGN |SC_PKCS15_PRKEY_USAGE_SIGNRECOVER; } if (ckis[i].cert_keyUsage & SC_X509_NON_REPUDIATION) { ckis[i].pub_usage |= SC_PKCS15_PRKEY_USAGE_ENCRYPT /* extra */ |SC_PKCS15_PRKEY_USAGE_NONREPUDIATION |SC_PKCS15_PRKEY_USAGE_VERIFY |SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER; ckis[i].priv_usage |= SC_PKCS15_PRKEY_USAGE_DECRYPT /*extra*/ |SC_PKCS15_PRKEY_USAGE_NONREPUDIATION |SC_PKCS15_PRKEY_USAGE_SIGN |SC_PKCS15_PRKEY_USAGE_SIGNRECOVER; } if (ckis[i].cert_keyUsage & SC_X509_KEY_ENCIPHERMENT) { ckis[i].pub_usage |= SC_PKCS15_PRKEY_USAGE_ENCRYPT| SC_PKCS15_PRKEY_USAGE_WRAP; ckis[i].priv_usage |= SC_PKCS15_PRKEY_USAGE_DECRYPT| SC_PKCS15_PRKEY_USAGE_UNWRAP; } if (ckis[i].cert_keyUsage & SC_X509_DATA_ENCIPHERMENT) { ckis[i].pub_usage |= SC_PKCS15_PRKEY_USAGE_ENCRYPT; ckis[i].priv_usage |= SC_PKCS15_PRKEY_USAGE_DECRYPT; } if (ckis[i].cert_keyUsage & SC_X509_KEY_AGREEMENT) { ckis[i].pub_usage |= SC_PKCS15_PRKEY_USAGE_DERIVE; ckis[i].priv_usage |= SC_PKCS15_PRKEY_USAGE_DERIVE; } if (ckis[i].cert_keyUsage & SC_X509_KEY_CERT_SIGN) { ckis[i].pub_usage |= SC_PKCS15_PRKEY_USAGE_VERIFY|SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER; ckis[i].priv_usage |= SC_PKCS15_PRKEY_USAGE_SIGN; } if (ckis[i].cert_keyUsage & SC_X509_CRL_SIGN) { ckis[i].pub_usage |= SC_PKCS15_PRKEY_USAGE_VERIFY|SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER; ckis[i].priv_usage |= SC_PKCS15_PRKEY_USAGE_SIGN; } if (ckis[i].cert_keyUsage & SC_X509_ENCIPHER_ONLY) { ckis[i].pub_usage |= SC_PKCS15_PRKEY_USAGE_ENCRYPT|SC_PKCS15_PRKEY_USAGE_WRAP; ckis[i].priv_usage |= SC_PKCS15_PRKEY_USAGE_DECRYPT|SC_PKCS15_PRKEY_USAGE_UNWRAP; } if (ckis[i].cert_keyUsage & SC_X509_DECIPHER_ONLY) { /* TODO is this correct */ ckis[i].pub_usage |= SC_PKCS15_PRKEY_USAGE_DECRYPT|SC_PKCS15_PRKEY_USAGE_UNWRAP; ckis[i].priv_usage |= SC_PKCS15_PRKEY_USAGE_ENCRYPT|SC_PKCS15_PRKEY_USAGE_WRAP; } } break; case SC_ALGORITHM_EC: 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) { ckis[i].pub_usage |= SC_PKCS15_PRKEY_USAGE_VERIFY; ckis[i].priv_usage |= SC_PKCS15_PRKEY_USAGE_SIGN; } if (ckis[i].cert_keyUsage & SC_X509_NON_REPUDIATION) { ckis[i].pub_usage |= SC_PKCS15_PRKEY_USAGE_NONREPUDIATION; ckis[i].priv_usage |= SC_PKCS15_PRKEY_USAGE_NONREPUDIATION; } if (ckis[i].cert_keyUsage & SC_X509_KEY_ENCIPHERMENT) { ckis[i].pub_usage |= 0; ckis[i].priv_usage |= 0; } if (ckis[i].cert_keyUsage & SC_X509_DATA_ENCIPHERMENT) { ckis[i].pub_usage |= 0; ckis[i].priv_usage |= 0; } if (ckis[i].cert_keyUsage & SC_X509_KEY_AGREEMENT) { ckis[i].pub_usage |= SC_PKCS15_PRKEY_USAGE_DERIVE; ckis[i].priv_usage |= SC_PKCS15_PRKEY_USAGE_DERIVE; } if (ckis[i].cert_keyUsage & SC_X509_KEY_CERT_SIGN) { ckis[i].pub_usage |= SC_PKCS15_PRKEY_USAGE_VERIFY; ckis[i].priv_usage |= SC_PKCS15_PRKEY_USAGE_SIGN; } if (ckis[i].cert_keyUsage & SC_X509_CRL_SIGN) { ckis[i].pub_usage |= SC_PKCS15_PRKEY_USAGE_VERIFY; ckis[i].priv_usage |= SC_PKCS15_PRKEY_USAGE_SIGN; } if (ckis[i].cert_keyUsage & SC_X509_ENCIPHER_ONLY) { ckis[i].pub_usage |= 0; ckis[i].priv_usage |= 0; } if (ckis[i].cert_keyUsage & SC_X509_DECIPHER_ONLY) { ckis[i].pub_usage |= 0; ckis[i].priv_usage |= 0; } } break; default: sc_log(card->ctx, "Unsupported key.algorithm %lu", cert_out->key->algorithm); ckis[i].pubkey_len = 0; /* set some value for now */ } 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_log(card->ctx, " Failed to add cert obj r=%d",r); continue; } } /* set 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)); 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; pin_info.tries_left = pins[i].tries_left; sc_format_path(pins[i].path, &pin_info.path); label = pins[i].label; 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; label = "Global PIN"; } strncpy(pin_obj.label, label, SC_PKCS15_MAX_LABEL_SIZE - 1); pin_obj.flags = pins[i].obj_flags; if ((i == 0 || pins[i].cardmod)) { /* * according to description of "RESET RETRY COUNTER" * command in specs PUK can only unblock PIV PIN */ pin_obj.auth_id.len = 1; pin_obj.auth_id.value[0] = 2; } r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info); LOG_TEST_GOTO_ERR(card->ctx, r, "Failed to add PIN"); } /* set public keys */ /* 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 = NULL; memset(&pubkey_info, 0, sizeof(pubkey_info)); memset(&pubkey_obj, 0, sizeof(pubkey_obj)); sc_pkcs15_format_id(pubkeys[i].id, &pubkey_info.id); pubkey_info.native = 1; pubkey_info.key_reference = pubkeys[i].ref; // sc_format_path(pubkeys[i].path, &pubkey_info.path); 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 stashed the pubkey * so we can use it when generating a certificate request * 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_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_log(card->ctx, "DEE look for env %s", pubkeys[i].getenvname?pubkeys[i].getenvname:"NULL"); if (pubkeys[i].getenvname == NULL) continue; filename = getenv(pubkeys[i].getenvname); sc_log(card->ctx, "DEE look for file %s", filename?filename:"NULL"); if (filename == NULL) continue; 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) { 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_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. * * We will cache it using the PKCS15 emulation objects */ pubkey_info.path.len = 0; ckis[i].key_alg = p15_key->algorithm; switch (p15_key->algorithm) { case SC_ALGORITHM_RSA: /* save pubkey_len in pub and priv */ ckis[i].pubkey_len = p15_key->u.rsa.modulus.len * 8; ckis[i].pubkey_found = 1; ckis[i].pubkey_from_file = 1; break; case SC_ALGORITHM_EC: ckis[i].key_alg = SC_ALGORITHM_EC; 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_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_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_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) { pubkey_info.usage = ckis[i].pub_usage; } else { pubkey_info.usage = pubkeys[i].usage_rsa; } 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); LOG_TEST_GOTO_ERR(card->ctx, r, "Failed to add RSA pubkey"); ckis[i].pubkey_found = 1; break; case SC_ALGORITHM_EC: if (ckis[i].cert_keyUsage_present) { pubkey_info.usage = ckis[i].pub_usage; } else { pubkey_info.usage = pubkeys[i].usage_ec; } pubkey_info.field_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_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_log(card->ctx, "key_alg %lu not supported", ckis[i].key_alg); continue; } 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_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; memset(&prkey_info, 0, sizeof(prkey_info)); memset(&prkey_obj, 0, sizeof(prkey_obj)); 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; sc_format_path(prkeys[i].path, &prkey_info.path); 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; /* 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 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_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; } prkey_info.modulus_length= ckis[i].pubkey_len; r = sc_pkcs15emu_add_rsa_prkey(p15card, &prkey_obj, &prkey_info); break; case SC_ALGORITHM_EC: 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_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); break; default: sc_log(card->ctx, "Unsupported key_alg %lu", ckis[i].key_alg); r = 0; /* we just skip this one */ } 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; 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_card_t *card = p15card->card; sc_context_t *ctx = card->ctx; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); if (piv_detect_card(p15card)) return SC_ERROR_WRONG_CARD; return sc_pkcs15emu_piv_init(p15card); } OpenSC-0.26.1/src/libopensc/pkcs15-prkey.c000066400000000000000000000743531474147347300201300ustar00rootroot00000000000000/* * pkcs15-prkey.c: PKCS #15 private key functions * * Copyright (C) 2001, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #ifdef ENABLE_OPENSSL #include #include #include #include #include #include #if OPENSSL_VERSION_NUMBER >= 0x30000000L # include # include #endif #ifndef OPENSSL_NO_EC #include #endif #endif #include "internal.h" #include "asn1.h" #include "pkcs15.h" #include "common/compat_strlcpy.h" #include "aux-data.h" /* * 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 }, { "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 }, { NULL, 0, 0, 0, NULL, NULL } }; #define C_ASN1_COM_KEY_ATTR_SIZE 7 static const struct sc_asn1_entry c_asn1_com_key_attr[C_ASN1_COM_KEY_ATTR_SIZE] = { { "iD", SC_ASN1_PKCS15_ID, SC_ASN1_TAG_OCTET_STRING, 0, NULL, NULL }, { "usage", SC_ASN1_BIT_FIELD, SC_ASN1_TAG_BIT_STRING, 0, NULL, NULL }, { "native", SC_ASN1_BOOLEAN, SC_ASN1_TAG_BOOLEAN, SC_ASN1_OPTIONAL, NULL, NULL }, { "accessFlags", SC_ASN1_BIT_FIELD, SC_ASN1_TAG_BIT_STRING, SC_ASN1_OPTIONAL, NULL, NULL }, { "keyReference",SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, SC_ASN1_OPTIONAL, NULL, NULL }, /* Absent in PKCS#15-v1.1 but present in ISO 7816-15(2004-01-15)*/ { "algReference", SC_ASN1_STRUCT, SC_ASN1_CONS | SC_ASN1_CTX | 1, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; #define C_ASN1_COM_PRKEY_ATTR_SIZE 2 static const struct sc_asn1_entry c_asn1_com_prkey_attr[C_ASN1_COM_PRKEY_ATTR_SIZE] = { { "subjectName", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_EMPTY_ALLOWED | SC_ASN1_ALLOC | SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; #define C_ASN1_RSAKEY_ATTR_SIZE 4 static const struct sc_asn1_entry c_asn1_rsakey_attr[] = { { "value", SC_ASN1_PATH, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_EMPTY_ALLOWED, NULL, NULL }, { "modulusLength", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, { "keyInfo", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; #define C_ASN1_PRK_RSA_ATTR_SIZE 2 static const struct sc_asn1_entry c_asn1_prk_rsa_attr[C_ASN1_PRK_RSA_ATTR_SIZE] = { { "privateRSAKeyAttributes", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; #define C_ASN1_GOSTR3410KEY_ATTR_SIZE 5 static const struct sc_asn1_entry c_asn1_gostr3410key_attr[C_ASN1_GOSTR3410KEY_ATTR_SIZE] = { { "value", SC_ASN1_PATH, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, { "params_r3410", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, { "params_r3411", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, SC_ASN1_OPTIONAL, NULL, NULL }, { "params_28147", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; #define C_ASN1_PRK_GOSTR3410_ATTR_SIZE 2 static const struct sc_asn1_entry c_asn1_prk_gostr3410_attr[C_ASN1_PRK_GOSTR3410_ATTR_SIZE] = { { "privateGOSTR3410KeyAttributes", 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 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 } }; #define C_ASN1_PRK_ECC_ATTR 2 static const struct sc_asn1_entry c_asn1_prk_ecc_attr[C_ASN1_PRK_ECC_ATTR] = { { "privateECCKeyAttributes", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; #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 }, { "privateGOSTR3410Key", SC_ASN1_PKCS15_OBJECT, 4 | SC_ASN1_CTX | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; int sc_pkcs15_decode_prkdf_entry(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *obj, const u8 ** buf, size_t *buflen) { sc_context_t *ctx = p15card->card->ctx; struct sc_pkcs15_prkey_info info; int r, i, 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); struct sc_asn1_entry asn1_com_key_attr[C_ASN1_COM_KEY_ATTR_SIZE]; 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_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]; struct sc_asn1_entry asn1_prk_ecc_attr[C_ASN1_PRK_ECC_ATTR]; 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 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_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); sc_copy_asn1_entry(c_asn1_ecckey_attr, asn1_ecckey_attr); sc_copy_asn1_entry(c_asn1_com_prkey_attr, asn1_com_prkey_attr); sc_copy_asn1_entry(c_asn1_com_key_attr, asn1_com_key_attr); 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, &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_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_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_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); 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); for (i=0; iname; i++) sc_format_asn1_entry(asn1_supported_algorithms + i, &info.algo_refs[i], NULL, 0); sc_format_asn1_entry(asn1_com_key_attr + 5, asn1_supported_algorithms, NULL, 0); sc_format_asn1_entry(asn1_com_prkey_attr + 0, &info.subject.value, &info.subject.len, 0); /* Fill in defaults */ memset(&info, 0, sizeof(info)); info.key_reference = -1; info.native = 1; memset(gostr3410_params, 0, sizeof(gostr3410_params)); r = sc_asn1_decode_choice(ctx, asn1_prkey, *buf, *buflen, buf, buflen); if (r == SC_ERROR_ASN1_END_OF_CONTENTS) 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) { /* 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; if (info.modulus_length != 0 || info.params.len != 0) { r = SC_ERROR_INVALID_ASN1_OBJECT; goto err; } info.modulus_length = SC_PKCS15_GOSTR3410_KEYSIZE; 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; } keyinfo_gostparams = info.params.data; keyinfo_gostparams->gostr3410 = gostr3410_params[0]; keyinfo_gostparams->gostr3411 = gostr3410_params[1]; keyinfo_gostparams->gost28147 = gostr3410_params[2]; } else { 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->file_app) { r = SC_ERROR_INTERNAL; goto err; } r = sc_pkcs15_make_absolute_path(&p15card->file_app->path, &info.path); if (r < 0) { goto err; } } else { info.path.aid = p15card->app->ddo.aid; } sc_log(ctx, "PrivKey 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; /* Check the auth_id - if not present, try and find it in access rules */ if ((obj->flags & SC_PKCS15_CO_FLAG_PRIVATE) && (obj->auth_id.len == 0)) { sc_log(ctx, "Private key %s has no auth ID - checking AccessControlRules", sc_pkcs15_print_id(&info.id)); /* Search in the access_rules for an appropriate auth ID */ for (i = 0; i < SC_PKCS15_MAX_ACCESS_RULES; i++) { /* If access_mode is one of the private key usage modes */ if (obj->access_rules[i].access_mode & (SC_PKCS15_ACCESS_RULE_MODE_EXECUTE | SC_PKCS15_ACCESS_RULE_MODE_PSO_CDS | SC_PKCS15_ACCESS_RULE_MODE_PSO_DECRYPT | SC_PKCS15_ACCESS_RULE_MODE_INT_AUTH)) { if (obj->access_rules[i].auth_id.len != 0) { /* Found an auth ID to use for private key access */ obj->auth_id = obj->access_rules[i].auth_id; sc_log(ctx, "Auth ID found - %s", sc_pkcs15_print_id(&obj->auth_id)); break; } } } /* No auth ID found */ if (i == SC_PKCS15_MAX_ACCESS_RULES) sc_log(ctx, "Warning: No auth ID found"); } obj->data = malloc(sizeof(info)); if (obj->data == NULL) { 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)); 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, u8 **buf, size_t *buflen) { struct sc_asn1_entry asn1_com_key_attr[C_ASN1_COM_KEY_ATTR_SIZE]; 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_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]; struct sc_asn1_entry asn1_prk_ecc_attr[C_ASN1_PRK_ECC_ATTR]; 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 = { (struct sc_pkcs15_object *) obj, asn1_com_key_attr, asn1_com_prkey_attr, asn1_prk_rsa_attr }; struct sc_asn1_pkcs15_object gostr3410_prkey_obj = { (struct sc_pkcs15_object *) obj, asn1_com_key_attr, asn1_com_prkey_attr, asn1_prk_gostr3410_attr }; struct sc_asn1_pkcs15_object ecc_prkey_obj = { (struct sc_pkcs15_object *) obj, asn1_com_key_attr, asn1_com_prkey_attr, asn1_prk_ecc_attr }; struct sc_pkcs15_prkey_info *prkey = (struct sc_pkcs15_prkey_info *) obj->data; struct sc_pkcs15_keyinfo_gostparams *keyinfo_gostparams; int r, i; size_t af_len, usage_len; 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_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); sc_copy_asn1_entry(c_asn1_ecckey_attr, asn1_ecckey_attr); sc_copy_asn1_entry(c_asn1_com_prkey_attr, asn1_com_prkey_attr); sc_copy_asn1_entry(c_asn1_com_key_attr, asn1_com_key_attr); switch (obj->type) { case SC_PKCS15_TYPE_PRKEY_RSA: sc_format_asn1_entry(asn1_prkey + 0, &rsa_prkey_obj, NULL, 1); sc_format_asn1_entry(asn1_prk_rsa_attr + 0, asn1_rsakey_attr, NULL, 1); sc_format_asn1_entry(asn1_rsakey_attr + 0, &prkey->path, NULL, 1); sc_format_asn1_entry(asn1_rsakey_attr + 1, &prkey->modulus_length, NULL, 1); break; case SC_PKCS15_TYPE_PRKEY_EC: sc_format_asn1_entry(asn1_prkey + 1, &ecc_prkey_obj, NULL, 1); sc_format_asn1_entry(asn1_prk_ecc_attr + 0, asn1_ecckey_attr, NULL, 1); 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_GOSTR3410: 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)) { keyinfo_gostparams = prkey->params.data; sc_format_asn1_entry(asn1_gostr3410key_attr + 1, &keyinfo_gostparams->gostr3410, NULL, 1); sc_format_asn1_entry(asn1_gostr3410key_attr + 2, &keyinfo_gostparams->gostr3411, NULL, 1); sc_format_asn1_entry(asn1_gostr3410key_attr + 3, &keyinfo_gostparams->gost28147, NULL, 1); } break; default: sc_log(ctx, "Invalid private key type: %X", obj->type); LOG_FUNC_RETURN(ctx, SC_ERROR_INTERNAL); break; } sc_format_asn1_entry(asn1_com_key_attr + 0, &prkey->id, NULL, 1); usage_len = sizeof(prkey->usage); sc_format_asn1_entry(asn1_com_key_attr + 1, &prkey->usage, &usage_len, 1); if (prkey->native == 0) sc_format_asn1_entry(asn1_com_key_attr + 2, &prkey->native, NULL, 1); if (prkey->access_flags) { af_len = sizeof(prkey->access_flags); sc_format_asn1_entry(asn1_com_key_attr + 3, &prkey->access_flags, &af_len, 1); } if (prkey->key_reference >= 0) sc_format_asn1_entry(asn1_com_key_attr + 4, &prkey->key_reference, NULL, 1); for (i=0; ialgo_refs[i]; i++) { sc_log(ctx, "Encode algorithm(%i) %i", i, prkey->algo_refs[i]); sc_format_asn1_entry(asn1_supported_algorithms + i, &prkey->algo_refs[i], NULL, 1); } sc_format_asn1_entry(asn1_com_key_attr + 5, asn1_supported_algorithms, NULL, prkey->algo_refs[0] != 0); if (prkey->subject.value && prkey->subject.len) sc_format_asn1_entry(asn1_com_prkey_attr + 0, prkey->subject.value, &prkey->subject.len, 1); else memset(asn1_com_prkey_attr, 0, sizeof(asn1_com_prkey_attr)); r = sc_asn1_encode(ctx, asn1_prkey, buf, buflen); sc_log(ctx, "Key path %s", sc_print_path(&prkey->path)); return r; } int sc_pkcs15_prkey_attrs_from_cert(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *cert_object, struct sc_pkcs15_object **out_key_object) { struct sc_context *ctx = p15card->card->ctx; #ifdef ENABLE_OPENSSL struct sc_pkcs15_object *key_object = NULL; struct sc_pkcs15_prkey_info *key_info = NULL; X509 *x = NULL; BIO *mem = NULL; unsigned char *buff = NULL, *ptr = NULL; int rv; LOG_FUNC_CALLED(ctx); if (out_key_object) *out_key_object = NULL; rv = sc_pkcs15_find_prkey_by_id(p15card, &((struct sc_pkcs15_cert_info *)cert_object->data)->id, &key_object); if (rv == SC_ERROR_OBJECT_NOT_FOUND) LOG_FUNC_RETURN(ctx, SC_SUCCESS); LOG_TEST_RET(ctx, rv, "Find private key error"); key_info = (struct sc_pkcs15_prkey_info *) key_object->data; 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, (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) { 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) { 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) { 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) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Subject allocation error"); memcpy(key_info->subject.value, buff, rv); key_info->subject.len = rv; strlcpy(key_object->label, cert_object->label, sizeof(key_object->label)); rv = 0; if (x) X509_free(x); if (mem) BIO_free(mem); if (buff) OPENSSL_free(buff); ERR_clear_error(); if (out_key_object) *out_key_object = key_object; sc_log(ctx, "Subject %s", sc_dump_hex(key_info->subject.value, key_info->subject.len)); LOG_FUNC_RETURN(ctx, rv); #else LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); #endif } void sc_pkcs15_erase_prkey(struct sc_pkcs15_prkey *key) { if (!key) return; switch (key->algorithm) { case SC_ALGORITHM_RSA: free(key->u.rsa.modulus.data); free(key->u.rsa.exponent.data); free(key->u.rsa.d.data); free(key->u.rsa.p.data); free(key->u.rsa.q.data); free(key->u.rsa.iqmp.data); free(key->u.rsa.dmp1.data); free(key->u.rsa.dmq1.data); break; case SC_ALGORITHM_GOSTR3410: free(key->u.gostr3410.d.data); break; case SC_ALGORITHM_EC: free(key->u.ec.params.der.value); free(key->u.ec.params.named_curve); free(key->u.ec.privateD.data); free(key->u.ec.ecpointQ.value); break; case SC_ALGORITHM_EDDSA: free(key->u.eddsa.pubkey.value); key->u.eddsa.pubkey.value = NULL; key->u.eddsa.pubkey.len = 0; free(key->u.eddsa.value.value); key->u.eddsa.value.value = NULL; key->u.eddsa.value.len = 0; 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); sc_pkcs15_free_key_params(&key->params); sc_aux_data_free(&key->aux_data); free(key); } int sc_pkcs15_convert_bignum(sc_pkcs15_bignum_t *dst, const void *src) { #ifdef ENABLE_OPENSSL const BIGNUM *bn = (const BIGNUM *)src; if (bn == 0) return 0; dst->len = BN_num_bytes(bn); dst->data = malloc(dst->len); if (!dst->data) return 0; BN_bn2bin(bn, dst->data); return 1; #else return 0; #endif } int sc_pkcs15_convert_prkey(struct sc_pkcs15_prkey *pkcs15_key, void *evp_key) { #ifdef ENABLE_OPENSSL EVP_PKEY *pk = (EVP_PKEY *)evp_key; int pk_type; pk_type = EVP_PKEY_base_id(pk); switch (pk_type) { case EVP_PKEY_RSA: { struct sc_pkcs15_prkey_rsa *dst = &pkcs15_key->u.rsa; #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 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; } #if !defined(OPENSSL_NO_EC) case NID_id_GostR3410_2001: { struct sc_pkcs15_prkey_gostr3410 *dst = &pkcs15_key->u.gostr3410; pkcs15_key->algorithm = SC_ALGORITHM_GOSTR3410; #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; unsigned char buf[255]; size_t buflen = 255; int nid; pkcs15_key->algorithm = SC_ALGORITHM_EC; #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]; 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; } #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, 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) return SC_ERROR_OUT_OF_MEMORY; memcpy(dst->ecpointQ.value, buf, buflen); dst->ecpointQ.len = buflen; /* * 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. */ 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; dst->privateD.data = realloc(dst->privateD.data, dst->privateD.len + d); if (!dst->privateD.data) return SC_ERROR_OUT_OF_MEMORY; memmove(dst->privateD.data + d, dst->privateD.data, dst->privateD.len); memset(dst->privateD.data, 0, d); dst->privateD.len += d; } break; } #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; } return SC_SUCCESS; #else return SC_ERROR_NOT_IMPLEMENTED; #endif } OpenSC-0.26.1/src/libopensc/pkcs15-pteid.c000066400000000000000000000243541474147347300200770ustar00rootroot00000000000000/* * PKCS15 emulation layer for Portugal eID card. * * Copyright (C) 2016-2017, Nuno Goncalves * Copyright (C) 2009, Joao Poupino * Copyright (C) 2004, 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 * * Based on the PKCS#15 emulation layer for EstEID card by Martin Paljak * */ /* * The card has a valid PKCS#15 file system. However, the private keys * are missing the SC_PKCS15_CO_FLAG_PRIVATE flag and this causes problems * with some applications (i.e. they don't work). * * The three main objectives of the emulation layer are: * * 1. Add the necessary SC_PKCS15_CO_FLAG_PRIVATE flag to private keys. * 2. Hide "superfluous" PKCS#15 objects, e.g. PUKs (the user can't use them). * 3. Improve usability by providing more descriptive names for the PINs, Keys, etc. * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "common/compat_strlcpy.h" #include "internal.h" #include "pkcs15.h" static int pteid_detect_card(struct sc_card *card); static int dump_ef(sc_card_t * card, const char *path, u8 * buf, size_t * buf_len) { int rv; sc_file_t *file = NULL; sc_path_t scpath; sc_format_path(path, &scpath); rv = sc_select_file(card, &scpath, &file); if (rv < 0) { sc_file_free(file); return rv; } if (file->size > *buf_len) { sc_file_free(file); return SC_ERROR_BUFFER_TOO_SMALL; } rv = sc_read_binary(card, 0, buf, file->size, 0); sc_file_free(file); if (rv < 0) return rv; *buf_len = rv; return SC_SUCCESS; } static const struct sc_asn1_entry c_asn1_odf[] = { {"privateKeys", SC_ASN1_STRUCT, SC_ASN1_CTX | 0 | SC_ASN1_CONS, 0, NULL, NULL}, {"publicKeys", SC_ASN1_STRUCT, SC_ASN1_CTX | 1 | SC_ASN1_CONS, 0, NULL, NULL}, {"trustedPublicKeys", SC_ASN1_STRUCT, SC_ASN1_CTX | 2 | SC_ASN1_CONS, 0, NULL, NULL}, {"secretKeys", SC_ASN1_STRUCT, SC_ASN1_CTX | 3 | SC_ASN1_CONS, 0, NULL, NULL}, {"certificates", SC_ASN1_STRUCT, SC_ASN1_CTX | 4 | SC_ASN1_CONS, 0, NULL, NULL}, {"trustedCertificates", SC_ASN1_STRUCT, SC_ASN1_CTX | 5 | SC_ASN1_CONS, 0, NULL, NULL}, {"usefulCertificates", SC_ASN1_STRUCT, SC_ASN1_CTX | 6 | SC_ASN1_CONS, 0, NULL, NULL}, {"dataObjects", SC_ASN1_STRUCT, SC_ASN1_CTX | 7 | SC_ASN1_CONS, 0, NULL, NULL}, {"authObjects", SC_ASN1_STRUCT, SC_ASN1_CTX | 8 | SC_ASN1_CONS, 0, NULL, NULL}, {NULL, 0, 0, 0, NULL, NULL} }; static const unsigned int odf_indexes[] = { SC_PKCS15_PRKDF, //0 SC_PKCS15_PUKDF, //1 SC_PKCS15_PUKDF_TRUSTED, //2 SC_PKCS15_SKDF, //3 SC_PKCS15_CDF, //4 SC_PKCS15_CDF_TRUSTED, //5 SC_PKCS15_CDF_USEFUL, //6 SC_PKCS15_DODF, //7 SC_PKCS15_AODF, //8 }; static int parse_odf(const u8 * buf, size_t buflen, struct sc_pkcs15_card *p15card) { const u8 *p = buf; size_t left = buflen; int r, i, type; sc_path_t path; struct sc_asn1_entry asn1_obj_or_path[] = { {"path", SC_ASN1_PATH, SC_ASN1_CONS | SC_ASN1_SEQUENCE, 0, &path, NULL}, {NULL, 0, 0, 0, NULL, NULL} }; struct sc_asn1_entry asn1_odf[10]; sc_path_t path_prefix; sc_format_path("3F004F00", &path_prefix); sc_copy_asn1_entry(c_asn1_odf, asn1_odf); for (i = 0; asn1_odf[i].name != NULL; i++) sc_format_asn1_entry(asn1_odf + i, asn1_obj_or_path, NULL, 0); while (left > 0) { r = sc_asn1_decode_choice(p15card->card->ctx, asn1_odf, p, left, &p, &left); if (r == SC_ERROR_ASN1_END_OF_CONTENTS) break; if (r < 0) return r; type = r; r = sc_pkcs15_make_absolute_path(&path_prefix, &path); if (r < 0) return r; r = sc_pkcs15_add_df(p15card, odf_indexes[type], &path); if (r) return r; } return 0; } static int sc_pkcs15emu_pteid_init(sc_pkcs15_card_t * p15card) { u8 buf[1024]; sc_pkcs15_df_t *df; sc_pkcs15_object_t *p15_obj; sc_path_t path; struct sc_file *file = NULL; size_t len; int rv; int i; sc_context_t *ctx = p15card->card->ctx; LOG_FUNC_CALLED(ctx); /* Check for correct card atr */ if (pteid_detect_card(p15card->card) != SC_SUCCESS) return SC_ERROR_WRONG_CARD; sc_log(p15card->card->ctx, "Selecting application DF"); sc_format_path("4F00", &path); rv = sc_select_file(p15card->card, &path, &file); if (rv != SC_SUCCESS || !file) return SC_ERROR_INTERNAL; /* set the application DF */ sc_file_free(p15card->file_app); p15card->file_app = file; /* Load TokenInfo */ len = sizeof(buf); rv = dump_ef(p15card->card, "4F005032", buf, &len); if (rv != SC_SUCCESS) { sc_log(ctx, "Reading of EF.TOKENINFO failed: %d", rv); LOG_FUNC_RETURN(ctx, rv); } 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); } p15card->tokeninfo->flags |= SC_PKCS15_TOKEN_PRN_GENERATION | SC_PKCS15_TOKEN_EID_COMPLIANT | SC_PKCS15_TOKEN_READONLY; /* Load ODF */ len = sizeof(buf); rv = dump_ef(p15card->card, "4F005031", buf, &len); if (rv != SC_SUCCESS) { sc_log(ctx, "Reading of ODF failed: %d", rv); LOG_FUNC_RETURN(ctx, rv); } 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); } /* Decode EF.PrKDF, EF.PuKDF, EF.CDF and EF.AODF */ for (df = p15card->df_list; df != NULL; df = df->next) { if (df->type == SC_PKCS15_PRKDF) { rv = sc_pkcs15_parse_df(p15card, df); if (rv != SC_SUCCESS) { sc_log(ctx, "Decoding of EF.PrKDF (%s) failed: %d", sc_print_path(&df->path), rv); } } if (df->type == SC_PKCS15_PUKDF) { rv = sc_pkcs15_parse_df(p15card, df); if (rv != SC_SUCCESS) { sc_log(ctx, "Decoding of EF.PuKDF (%s) failed: %d", sc_print_path(&df->path), rv); } } if (df->type == SC_PKCS15_CDF) { rv = sc_pkcs15_parse_df(p15card, df); if (rv != SC_SUCCESS) { sc_log(ctx, "Decoding of EF.CDF (%s) failed: %d", sc_print_path(&df->path), rv); } } if (df->type == SC_PKCS15_AODF) { rv = sc_pkcs15_parse_df(p15card, df); if (rv != SC_SUCCESS) { sc_log(ctx, "Decoding of EF.AODF (%s) failed: %d", sc_print_path(&df->path), rv); } } } p15_obj = p15card->obj_list; while (p15_obj != NULL) { if ( p15_obj->df && (p15_obj->df->type == SC_PKCS15_PRKDF) ) { struct sc_pkcs15_prkey_info *prkey_info = (sc_pkcs15_prkey_info_t *) p15_obj->data; prkey_info->access_flags = SC_PKCS15_PRKEY_ACCESS_SENSITIVE | SC_PKCS15_PRKEY_ACCESS_ALWAYSSENSITIVE | SC_PKCS15_PRKEY_ACCESS_NEVEREXTRACTABLE | SC_PKCS15_PRKEY_ACCESS_LOCAL; p15_obj->flags = SC_PKCS15_CO_FLAG_PRIVATE; } if ( p15_obj->df && (p15_obj->df->type == SC_PKCS15_AODF) ) { static const char *pteid_pin_names[3] = { "Auth PIN", "Sign PIN", "Address PIN" }; struct sc_pin_cmd_data pin_cmd_data; struct sc_pkcs15_auth_info *pin_info = (sc_pkcs15_auth_info_t *) p15_obj->data; 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; pin_info->tries_left = -1; pin_info->max_tries = 3; pin_info->auth_method = SC_AC_CHV; memset(&pin_cmd_data, 0, sizeof(pin_cmd_data)); 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); if (rv == SC_SUCCESS) { pin_info->tries_left = pin_cmd_data.pin1.tries_left; pin_info->logged_in = pin_cmd_data.pin1.logged_in; } } /* Remove found public keys as cannot be read_binary()'d */ if ( p15_obj->df && (p15_obj->df->type == SC_PKCS15_PUKDF) ) { sc_pkcs15_object_t *puk = p15_obj; p15_obj = p15_obj->next; sc_pkcs15_remove_object(p15card, puk); sc_pkcs15_free_object(puk); } else { p15_obj = p15_obj->next; } } /* Add data objects */ for (i = 0; i < 5; i++) { static const char *object_labels[5] = { "Trace", "Citizen Data", "Citizen Address Data", "SOd", "Citizen Notepad", }; static const char *object_authids[5] = {NULL, NULL, "3", NULL, NULL}; static const char *object_paths[5] = { "3f000003", "3f005f00ef02", "3f005f00ef05", "3f005f00ef06", "3f005f00ef07", }; static const int object_flags[5] = { 0, 0, SC_PKCS15_CO_FLAG_PRIVATE, 0, 0, }; 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_format_path(object_paths[i], &obj_info.path); strlcpy(obj_info.app_label, object_labels[i], SC_PKCS15_MAX_LABEL_SIZE); if (object_authids[i] != NULL) sc_pkcs15_format_id(object_authids[i], &obj_obj.auth_id); strlcpy(obj_obj.label, object_labels[i], SC_PKCS15_MAX_LABEL_SIZE); obj_obj.flags = object_flags[i]; rv = sc_pkcs15emu_object_add(p15card, SC_PKCS15_TYPE_DATA_OBJECT, &obj_obj, &obj_info); if (rv != SC_SUCCESS){ sc_log(ctx, "Object add failed: %d", rv); break; } } LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int pteid_detect_card(struct sc_card *card) { if (card->type == SC_CARD_TYPE_GEMSAFEV1_PTEID) return SC_SUCCESS; return SC_ERROR_WRONG_CARD; } 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); /* check for proper card */ r = pteid_detect_card(p15card->card); if (r == SC_ERROR_WRONG_CARD) LOG_FUNC_RETURN(ctx, SC_ERROR_WRONG_CARD); /* ok: initialize and return */ LOG_FUNC_RETURN(ctx, sc_pkcs15emu_pteid_init(p15card)); } OpenSC-0.26.1/src/libopensc/pkcs15-pubkey.c000066400000000000000000001652701474147347300202740ustar00rootroot00000000000000/* * pkcs15-pubkey.c: PKCS #15 public key functions * * 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., 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 #include #include #include #if OPENSSL_VERSION_NUMBER >= 0x30000000L # include # include #endif #ifndef OPENSSL_NO_EC #include #endif #endif #include "internal.h" #include "asn1.h" #include "pkcs15.h" #define C_ASN1_PKINFO_ATTR_SIZE 3 static const struct sc_asn1_entry c_asn1_pkinfo[C_ASN1_PKINFO_ATTR_SIZE] = { { "algorithm", SC_ASN1_ALGORITHM_ID, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, { "subjectPublicKey", SC_ASN1_BIT_STRING_NI, SC_ASN1_TAG_BIT_STRING, SC_ASN1_ALLOC, NULL, NULL}, { NULL, 0, 0, 0, NULL, NULL } }; #define C_ASN1_COM_KEY_ATTR_SIZE 6 static const struct sc_asn1_entry c_asn1_com_key_attr[C_ASN1_COM_KEY_ATTR_SIZE] = { { "iD", SC_ASN1_PKCS15_ID, SC_ASN1_TAG_OCTET_STRING, 0, NULL, NULL }, { "usage", SC_ASN1_BIT_FIELD, SC_ASN1_TAG_BIT_STRING, 0, NULL, NULL }, { "native", SC_ASN1_BOOLEAN, SC_ASN1_TAG_BOOLEAN, SC_ASN1_OPTIONAL, NULL, NULL }, { "accessFlags", SC_ASN1_BIT_FIELD, SC_ASN1_TAG_BIT_STRING, SC_ASN1_OPTIONAL, NULL, NULL }, { "keyReference",SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; #define C_ASN1_COM_PUBKEY_ATTR_SIZE 2 static const struct sc_asn1_entry c_asn1_com_pubkey_attr[C_ASN1_COM_PUBKEY_ATTR_SIZE] = { { "subjectName", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_EMPTY_ALLOWED | SC_ASN1_ALLOC | SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; #define C_ASN1_RSAKEY_VALUE_CHOICE_SIZE 3 static const struct sc_asn1_entry c_asn1_rsakey_value_choice[C_ASN1_RSAKEY_VALUE_CHOICE_SIZE] = { { "path", SC_ASN1_PATH, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_EMPTY_ALLOWED, NULL, NULL }, { "direct", SC_ASN1_OCTET_STRING, SC_ASN1_CTX | 0 | SC_ASN1_CONS, SC_ASN1_OPTIONAL | SC_ASN1_ALLOC, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; #define C_ASN1_RSAKEY_ATTR_SIZE 4 static const struct sc_asn1_entry c_asn1_rsakey_attr[C_ASN1_RSAKEY_ATTR_SIZE] = { { "value", SC_ASN1_CHOICE, 0, 0, NULL, NULL }, { "modulusLength", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, { "keyInfo", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; #define C_ASN1_ECKEY_VALUE_CHOICE_SIZE 3 static const struct sc_asn1_entry c_asn1_eckey_value_choice[C_ASN1_ECKEY_VALUE_CHOICE_SIZE] = { { "path", SC_ASN1_PATH, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_EMPTY_ALLOWED, NULL, NULL }, { "direct", SC_ASN1_OCTET_STRING, SC_ASN1_CTX | 0 | SC_ASN1_CONS, SC_ASN1_OPTIONAL | SC_ASN1_ALLOC, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; #define C_ASN1_ECKEY_ATTR_SIZE 3 static const struct sc_asn1_entry c_asn1_eckey_attr[C_ASN1_ECKEY_ATTR_SIZE] = { { "value", SC_ASN1_CHOICE, 0, 0, NULL, NULL }, { "keyInfo", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; #define C_ASN1_RSA_TYPE_ATTR_SIZE 2 static const struct sc_asn1_entry c_asn1_rsa_type_attr[C_ASN1_RSA_TYPE_ATTR_SIZE] = { { "publicRSAKeyAttributes", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; #define C_ASN1_EC_TYPE_ATTR_SIZE 2 static const struct sc_asn1_entry c_asn1_ec_type_attr[C_ASN1_EC_TYPE_ATTR_SIZE] = { { "publicECKeyAttributes", 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 }, { "params_r3410", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, { "params_r3411", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, SC_ASN1_OPTIONAL, NULL, NULL }, { "params_28147", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; #define C_ASN1_GOST3410_TYPE_ATTR_SIZE 2 static const struct sc_asn1_entry c_asn1_gostr3410_type_attr[C_ASN1_GOST3410_TYPE_ATTR_SIZE] = { { "publicGOSTR3410KeyAttributes", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; #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 }, { "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 */ { NULL, 0, 0, 0, NULL, NULL } }; #define C_ASN1_PUBKEY_SIZE 2 static const struct sc_asn1_entry c_asn1_pubkey[C_ASN1_PUBKEY_SIZE] = { { "publicKey", SC_ASN1_CHOICE, 0, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; int sc_pkcs15_pubkey_from_spki_sequence(sc_context_t *ctx, const u8 *buf, size_t buflen, sc_pkcs15_pubkey_t ** outpubkey); int sc_pkcs15_decode_pubkey_direct_value(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *obj) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_pubkey_info *info = (struct sc_pkcs15_pubkey_info *) obj->data; LOG_FUNC_CALLED(ctx); if (obj->content.value == NULL || obj->content.len == 0) LOG_FUNC_RETURN(ctx, SC_SUCCESS); if (*obj->content.value == (SC_ASN1_TAG_CONSTRUCTED | SC_ASN1_TAG_SEQUENCE)) { /* RAW direct value */ sc_log(ctx, "Decoding 'RAW' direct value"); info->direct.raw.value = malloc(obj->content.len); if (!info->direct.raw.value) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); memcpy(info->direct.raw.value, obj->content.value, obj->content.len); info->direct.raw.len = obj->content.len; /* TODO: encode 'spki' direct value */ } if (*obj->content.value == (SC_ASN1_TAG_CONTEXT | SC_ASN1_TAG_CONSTRUCTED | 0x01)) { struct sc_pkcs15_pubkey *pubkey = NULL; int rv; /* SPKI direct value */ sc_log(ctx, "Decoding 'SPKI' direct value"); info->direct.spki.value = malloc(obj->content.len); if (!info->direct.spki.value) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); memcpy(info->direct.spki.value, obj->content.value, obj->content.len); info->direct.spki.len = obj->content.len; rv = sc_pkcs15_pubkey_from_spki_sequence(ctx, info->direct.spki.value, info->direct.spki.len, &pubkey); 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); sc_pkcs15_free_pubkey(pubkey); LOG_TEST_RET(ctx, rv, "Failed to encode 'RAW' direct value"); } LOG_FUNC_RETURN(ctx, SC_SUCCESS); } int sc_pkcs15_decode_pukdf_entry(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *obj, const u8 ** buf, size_t *buflen) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_pubkey_info *info; int r, gostr3410_params[3]; struct sc_pkcs15_keyinfo_gostparams *keyinfo_gostparams; 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]; struct sc_asn1_entry asn1_rsakey_value_choice[C_ASN1_RSAKEY_VALUE_CHOICE_SIZE]; struct sc_asn1_entry asn1_rsakey_attr[C_ASN1_RSAKEY_ATTR_SIZE]; struct sc_asn1_entry asn1_rsa_type_attr[C_ASN1_RSA_TYPE_ATTR_SIZE]; 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_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]; struct sc_asn1_entry asn1_pubkey[C_ASN1_PUBKEY_SIZE]; struct sc_asn1_pkcs15_object rsakey_obj = { obj, asn1_com_key_attr, 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 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); sc_copy_asn1_entry(c_asn1_rsakey_value_choice, asn1_rsakey_value_choice); sc_copy_asn1_entry(c_asn1_rsakey_attr, asn1_rsakey_attr); 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_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_pubkey_choice + 0, &rsakey_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 + 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_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 + 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_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 + 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_pubkey + 0, asn1_pubkey_choice, NULL, 0); /* Fill in defaults */ 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) 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[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) { r = SC_ERROR_OUT_OF_MEMORY; goto err; } assert(sizeof(*keyinfo_gostparams) == info->params.len); 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[2].flags & SC_ASN1_PRESENT) { obj->type = SC_PKCS15_TYPE_PUBKEY_EC; } else { goto err; } 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) { goto err; } } else { info->path.aid = p15card->app->ddo.aid; } 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; obj->data = info; info = NULL; r = sc_pkcs15_decode_pubkey_direct_value(p15card, obj); if (r < 0) { info = obj->data; obj->data = NULL; } LOG_TEST_GOTO_ERR(ctx, r, "Decode public key direct value failed"); 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); } int sc_pkcs15_encode_pukdf_entry(struct sc_context *ctx, const struct sc_pkcs15_object *obj, unsigned char **buf, size_t *buflen) { 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]; struct sc_asn1_entry asn1_rsakey_value_choice[C_ASN1_RSAKEY_VALUE_CHOICE_SIZE]; struct sc_asn1_entry asn1_rsakey_attr[C_ASN1_RSAKEY_ATTR_SIZE]; struct sc_asn1_entry asn1_rsa_type_attr[C_ASN1_RSA_TYPE_ATTR_SIZE]; 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_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]; struct sc_asn1_entry asn1_pubkey[C_ASN1_PUBKEY_SIZE]; struct sc_pkcs15_pubkey_info *pubkey = (struct sc_pkcs15_pubkey_info *) obj->data; struct sc_asn1_pkcs15_object rsakey_obj = { (struct sc_pkcs15_object *) obj, asn1_com_key_attr, asn1_com_pubkey_attr, asn1_rsa_type_attr }; 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 gostr3410key_obj = { (struct sc_pkcs15_object *) obj, asn1_com_key_attr, asn1_com_pubkey_attr, asn1_gostr3410_type_attr }; struct sc_pkcs15_keyinfo_gostparams *keyinfo_gostparams; int r; size_t af_len, usage_len; unsigned char *spki_value = NULL; 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); sc_copy_asn1_entry(c_asn1_rsakey_value_choice, asn1_rsakey_value_choice); sc_copy_asn1_entry(c_asn1_rsakey_attr, asn1_rsakey_attr); 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_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); switch (obj->type) { case SC_PKCS15_TYPE_PUBKEY_RSA: sc_format_asn1_entry(asn1_pubkey_choice + 0, &rsakey_obj, NULL, 1); sc_format_asn1_entry(asn1_rsa_type_attr + 0, asn1_rsakey_attr, NULL, 1); if (pubkey->path.len) { sc_format_asn1_entry(asn1_rsakey_value_choice + 0, &pubkey->path, NULL, 1); } else if (pubkey->direct.raw.value && pubkey->direct.raw.len) { /* In RSAPublicKeyChoice 'raw' value keep it's SEQUENCE tag */ sc_log(ctx, "Encode direct 'RAW' value"); sc_format_asn1_entry(asn1_rsakey_value_choice + 1, pubkey->direct.raw.value, (void *)&pubkey->direct.raw.len, 1); } else if (pubkey->direct.spki.value && pubkey->direct.spki.len) { /* In RSAPublicKeyChoice 'spki' value changes initial SEQUENCE tag for * CONTEXT [1] constructed SEQUENCE */ sc_log(ctx, "Encode direct 'SPKI' value"); spki_value = malloc(pubkey->direct.spki.len); if (!spki_value) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); memcpy(spki_value, pubkey->direct.spki.value, pubkey->direct.spki.len); *spki_value = (SC_ASN1_TAG_CONTEXT | SC_ASN1_TAG_CONSTRUCTED | 0x01); sc_format_asn1_entry(asn1_rsakey_value_choice + 1, spki_value, (void *)&pubkey->direct.spki.len, 1); } else if (obj->content.value && obj->content.len) { sc_log(ctx, "Encode 'RAW' object content"); sc_format_asn1_entry(asn1_rsakey_value_choice + 1, obj->content.value, (void *)&obj->content.len, 1); } else { sc_log(ctx, "Use empty path"); sc_format_asn1_entry(asn1_rsakey_value_choice + 0, &pubkey->path, NULL, 1); } 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_GOSTR3410: 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); sc_format_asn1_entry(asn1_gostr3410key_attr + 0, &pubkey->path, NULL, 1); if (pubkey->params.len == sizeof(*keyinfo_gostparams)) { keyinfo_gostparams = pubkey->params.data; sc_format_asn1_entry(asn1_gostr3410key_attr + 1, &keyinfo_gostparams->gostr3410, NULL, 1); sc_format_asn1_entry(asn1_gostr3410key_attr + 2, &keyinfo_gostparams->gostr3411, NULL, 1); sc_format_asn1_entry(asn1_gostr3410key_attr + 3, &keyinfo_gostparams->gost28147, NULL, 1); } break; case SC_PKCS15_TYPE_PUBKEY_EC: 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); if (pubkey->path.len) { sc_format_asn1_entry(asn1_eckey_value_choice + 0, &pubkey->path, NULL, 1); } else if (pubkey->direct.spki.value) { sc_format_asn1_entry(asn1_eckey_value_choice + 1, pubkey->direct.spki.value, (void *)&pubkey->direct.spki.len, 1); } else if (pubkey->direct.raw.value) { sc_format_asn1_entry(asn1_eckey_value_choice + 1, pubkey->direct.raw.value, (void *)&pubkey->direct.raw.len, 1); LOG_TEST_RET(ctx, SC_ERROR_NOT_IMPLEMENTED, "Needs KeyInfo with reference to algorithm in TokenInfo"); } else if (obj->content.value) { sc_format_asn1_entry(asn1_eckey_value_choice + 1, obj->content.value, (void *)&obj->content.len, 1); LOG_TEST_RET(ctx, SC_ERROR_NOT_IMPLEMENTED, "Needs KeyInfo with reference to algorithm in TokenInfo"); } sc_format_asn1_entry(asn1_eckey_attr + 0, asn1_eckey_value_choice, NULL, 1); break; default: sc_log(ctx, "Unsupported public key type: %X", obj->type); LOG_FUNC_RETURN(ctx, SC_ERROR_INTERNAL); break; } sc_format_asn1_entry(asn1_com_key_attr + 0, &pubkey->id, NULL, 1); usage_len = sizeof(pubkey->usage); sc_format_asn1_entry(asn1_com_key_attr + 1, &pubkey->usage, &usage_len, 1); if (pubkey->native == 0) sc_format_asn1_entry(asn1_com_key_attr + 2, &pubkey->native, NULL, 1); if (pubkey->access_flags) { af_len = sizeof(pubkey->access_flags); sc_format_asn1_entry(asn1_com_key_attr + 3, &pubkey->access_flags, &af_len, 1); } if (pubkey->key_reference >= 0) sc_format_asn1_entry(asn1_com_key_attr + 4, &pubkey->key_reference, NULL, 1); sc_format_asn1_entry(asn1_pubkey + 0, asn1_pubkey_choice, NULL, 1); if (pubkey->subject.value && pubkey->subject.len) sc_format_asn1_entry(asn1_com_pubkey_attr + 0, pubkey->subject.value, &pubkey->subject.len, 1); else memset(asn1_com_pubkey_attr, 0, sizeof(asn1_com_pubkey_attr)); r = sc_asn1_encode(ctx, asn1_pubkey, buf, buflen); sc_log(ctx, "Key path %s", sc_print_path(&pubkey->path)); if (spki_value) free(spki_value); return r; } #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 }, { NULL, 0, 0, 0, NULL, NULL } }; #define C_ASN1_RSA_PUB_COEFFICIENTS_SIZE 3 static struct sc_asn1_entry c_asn1_rsa_pub_coefficients[C_ASN1_RSA_PUB_COEFFICIENTS_SIZE] = { { "modulus", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_INTEGER, SC_ASN1_ALLOC|SC_ASN1_UNSIGNED, NULL, NULL }, { "exponent", 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 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 }, { NULL, 0, 0, 0, NULL, NULL } }; #define C_ASN1_EDDSA_PUBKEY_SIZE 2 static struct sc_asn1_entry c_asn1_eddsa_pubkey[C_ASN1_EDDSA_PUBKEY_SIZE] = { { "pubkey", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_OCTET_STRING, SC_ASN1_ALLOC, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; int sc_pkcs15_decode_pubkey_rsa(sc_context_t *ctx, struct sc_pkcs15_pubkey_rsa *key, const u8 *buf, size_t buflen) { struct sc_asn1_entry asn1_public_key[C_ASN1_PUBLIC_KEY_SIZE]; struct sc_asn1_entry asn1_rsa_pub_coefficients[C_ASN1_RSA_PUB_COEFFICIENTS_SIZE]; int r; LOG_FUNC_CALLED(ctx); sc_copy_asn1_entry(c_asn1_public_key, asn1_public_key); sc_format_asn1_entry(asn1_public_key + 0, asn1_rsa_pub_coefficients, NULL, 0); sc_copy_asn1_entry(c_asn1_rsa_pub_coefficients, asn1_rsa_pub_coefficients); sc_format_asn1_entry(asn1_rsa_pub_coefficients + 0, &key->modulus.data, &key->modulus.len, 0); sc_format_asn1_entry(asn1_rsa_pub_coefficients + 1, &key->exponent.data, &key->exponent.len, 0); r = sc_asn1_decode(ctx, asn1_public_key, buf, buflen, NULL, NULL); LOG_TEST_RET(ctx, r, "ASN.1 parsing of public key failed"); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } int sc_pkcs15_encode_pubkey_rsa(sc_context_t *ctx, struct sc_pkcs15_pubkey_rsa *key, u8 **buf, size_t *buflen) { struct sc_asn1_entry asn1_public_key[C_ASN1_PUBLIC_KEY_SIZE]; struct sc_asn1_entry asn1_rsa_pub_coefficients[C_ASN1_RSA_PUB_COEFFICIENTS_SIZE]; int r; LOG_FUNC_CALLED(ctx); sc_copy_asn1_entry(c_asn1_public_key, asn1_public_key); sc_format_asn1_entry(asn1_public_key + 0, asn1_rsa_pub_coefficients, NULL, 1); sc_copy_asn1_entry(c_asn1_rsa_pub_coefficients, asn1_rsa_pub_coefficients); sc_format_asn1_entry(asn1_rsa_pub_coefficients + 0, key->modulus.data, &key->modulus.len, 1); sc_format_asn1_entry(asn1_rsa_pub_coefficients + 1, key->exponent.data, &key->exponent.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) { struct sc_asn1_entry asn1_gostr3410_pub_coeff[C_ASN1_GOSTR3410_PUB_COEFFICIENTS_SIZE]; int r; struct sc_object_id param_key = {{ 1, 2, 643, 2, 2, 35, 1, -1}}; struct sc_object_id param_hash = {{ 1, 2, 643, 2, 2, 30, 1, -1}}; LOG_FUNC_CALLED(ctx); sc_copy_asn1_entry(c_asn1_gostr3410_pub_coefficients, asn1_gostr3410_pub_coeff); sc_format_asn1_entry(asn1_gostr3410_pub_coeff + 0, &key->xy.data, &key->xy.len, 0); r = sc_asn1_decode(ctx, asn1_gostr3410_pub_coeff, buf, buflen, NULL, NULL); LOG_TEST_RET(ctx, r, "ASN.1 parsing of public key failed"); key->params.key = param_key; key->params.hash = param_hash; LOG_FUNC_RETURN(ctx, SC_SUCCESS); } int sc_pkcs15_encode_pubkey_gostr3410(sc_context_t *ctx, struct sc_pkcs15_pubkey_gostr3410 *key, u8 **buf, size_t *buflen) { struct sc_asn1_entry asn1_gostr3410_pub_coeff[C_ASN1_GOSTR3410_PUB_COEFFICIENTS_SIZE]; int r; LOG_FUNC_CALLED(ctx); sc_copy_asn1_entry(c_asn1_gostr3410_pub_coefficients, asn1_gostr3410_pub_coeff); sc_format_asn1_entry(asn1_gostr3410_pub_coeff + 0, key->xy.data, &key->xy.len, 1); r = sc_asn1_encode(ctx, asn1_gostr3410_pub_coeff, buf, buflen); LOG_TEST_RET(ctx, r, "ASN.1 encoding failed"); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } /* * We are storing the ec_pointQ as u8 string. not as DER */ int sc_pkcs15_decode_pubkey_ec(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; 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) { free(ecpoint_data); LOG_TEST_RET(ctx, r, "ASN.1 decoding failed"); } if (ecpoint_len == 0 || *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); } int 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]; 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); LOG_FUNC_RETURN(ctx, sc_asn1_encode(ctx, asn1_ec_pointQ, buf, buflen)); } /* * EdDSA keys are just byte strings. For now only * for Ed25519 keys 32B length are supported */ int sc_pkcs15_decode_pubkey_eddsa(sc_context_t *ctx, struct sc_pkcs15_pubkey_eddsa *key, const u8 *buf, size_t buflen) { int r; u8 * pubkey = NULL; size_t pubkey_len; struct sc_asn1_entry asn1_eddsa_pubkey[C_ASN1_EDDSA_PUBKEY_SIZE]; LOG_FUNC_CALLED(ctx); sc_copy_asn1_entry(c_asn1_eddsa_pubkey, asn1_eddsa_pubkey); sc_format_asn1_entry(asn1_eddsa_pubkey + 0, &pubkey, &pubkey_len, 1); r = sc_asn1_decode(ctx, asn1_eddsa_pubkey, buf, buflen, NULL, NULL); if (r < 0) LOG_TEST_RET(ctx, r, "ASN.1 decoding failed"); key->pubkey.len = pubkey_len; key->pubkey.value = pubkey; LOG_FUNC_RETURN(ctx, SC_SUCCESS); } int sc_pkcs15_encode_pubkey_eddsa(sc_context_t *ctx, struct sc_pkcs15_pubkey_eddsa *key, u8 **buf, size_t *buflen) { struct sc_asn1_entry asn1_eddsa_pubkey[C_ASN1_EDDSA_PUBKEY_SIZE]; LOG_FUNC_CALLED(ctx); sc_copy_asn1_entry(c_asn1_eddsa_pubkey, asn1_eddsa_pubkey); sc_format_asn1_entry(asn1_eddsa_pubkey + 0, key->pubkey.value, &key->pubkey.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, u8 **buf, size_t *len) { if (key->algorithm == SC_ALGORITHM_RSA) return sc_pkcs15_encode_pubkey_rsa(ctx, &key->u.rsa, 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) /* XXX encoding is the same here */ return sc_pkcs15_encode_pubkey_eddsa(ctx, &key->u.eddsa, buf, len); sc_log(ctx, "Encoding of public key type %lu not supported", key->algorithm); LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); } static const struct sc_asn1_entry c_asn1_spki_key_items[] = { { "algorithm", SC_ASN1_ALGORITHM_ID, SC_ASN1_CONS| SC_ASN1_TAG_SEQUENCE, 0, NULL, NULL}, { "key", SC_ASN1_BIT_STRING_NI, SC_ASN1_TAG_BIT_STRING, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; static const struct sc_asn1_entry c_asn1_spki_key[] = { { "publicKey", SC_ASN1_STRUCT, SC_ASN1_CONS | SC_ASN1_TAG_SEQUENCE, 0, NULL, NULL}, { NULL, 0, 0, 0, NULL, NULL } }; /* * Encode a pubkey as a SPKI, useful for pkcs15-tool, and for PKCS#15 files. */ int sc_pkcs15_encode_pubkey_as_spki(sc_context_t *ctx, struct sc_pkcs15_pubkey *pubkey, u8 **buf, size_t *len) { int r = 0; struct sc_asn1_entry asn1_spki_key[2], asn1_spki_key_items[3]; struct sc_pkcs15_u8 pkey; size_t key_len; LOG_FUNC_CALLED(ctx); pkey.value = NULL; pkey.len = 0; 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) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); sc_init_oid(&pubkey->alg_id->oid); pubkey->alg_id->algorithm = pubkey->algorithm; } switch (pubkey->algorithm) { case SC_ALGORITHM_EC: /* * most keys, but not EC have only one encoding. * For a SPKI, the ecpoint is placed directly in the * BIT STRING */ key_len = pubkey->u.ec.ecpointQ.len * 8; pkey.value = pubkey->u.ec.ecpointQ.value; pkey.len = 0; /* flag as do not delete */ if (pubkey->u.ec.params.named_curve || pubkey->u.ec.params.der.value) { struct sc_ec_parameters *ec_params = NULL; 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(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; /* This could have been already allocated: avoid memory leak */ sc_asn1_clear_algorithm_id(pubkey->alg_id); pubkey->alg_id->params = ec_params; } break; case SC_ALGORITHM_GOSTR3410: /* TODO is this needed? does it cause mem leak? */ pubkey->alg_id->params = &pubkey->u.gostr3410.params; r = sc_pkcs15_encode_pubkey(ctx, pubkey, &pkey.value, &pkey.len); key_len = pkey.len * 8; break; case SC_ALGORITHM_EDDSA: case SC_ALGORITHM_XEDDSA: /* For a SPKI, the pubkey is placed directly in the BIT STRING */ pkey.value = malloc(pubkey->u.eddsa.pubkey.len); memcpy(pkey.value, pubkey->u.eddsa.pubkey.value, pubkey->u.eddsa.pubkey.len); // Should be pkey.len = 0 there? key_len = pubkey->u.eddsa.pubkey.len * 8; break; default: r = sc_pkcs15_encode_pubkey(ctx, pubkey, &pkey.value, &pkey.len); key_len = pkey.len * 8; break; } if (r == 0) { sc_copy_asn1_entry(c_asn1_spki_key, asn1_spki_key); sc_copy_asn1_entry(c_asn1_spki_key_items, asn1_spki_key_items); sc_format_asn1_entry(asn1_spki_key + 0, asn1_spki_key_items, NULL, 1); sc_format_asn1_entry(asn1_spki_key_items + 0, pubkey->alg_id, NULL, 1); sc_format_asn1_entry(asn1_spki_key_items + 1, pkey.value, &key_len, 1); r = sc_asn1_encode(ctx, asn1_spki_key, buf, len); } if (pkey.len && pkey.value) free(pkey.value); LOG_FUNC_RETURN(ctx, r); } int sc_pkcs15_decode_pubkey(sc_context_t *ctx, struct sc_pkcs15_pubkey *key, const u8 *buf, size_t len) { if (key->algorithm == SC_ALGORITHM_RSA) return sc_pkcs15_decode_pubkey_rsa(ctx, &key->u.rsa, 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.eddsa, buf, len); sc_log(ctx, "Decoding of public key type %lu not supported", key->algorithm); return SC_ERROR_NOT_SUPPORTED; } /* * Read public key. */ int sc_pkcs15_read_pubkey(struct sc_pkcs15_card *p15card, const struct sc_pkcs15_object *obj, struct sc_pkcs15_pubkey **out) { struct sc_context *ctx; const struct sc_pkcs15_pubkey_info *info = NULL; struct sc_pkcs15_pubkey *pubkey = NULL; 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) { return SC_ERROR_INVALID_ARGUMENTS; } ctx = p15card->card->ctx; LOG_FUNC_CALLED(ctx); sc_log(ctx, "Public key type 0x%X", obj->type); switch (obj->type) { case SC_PKCS15_TYPE_PUBKEY_RSA: algorithm = SC_ALGORITHM_RSA; 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."); } info = (const struct sc_pkcs15_pubkey_info *) obj->data; pubkey = calloc(1, sizeof(struct sc_pkcs15_pubkey)); if (pubkey == NULL) { LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); } pubkey->algorithm = algorithm; /* starting from SPKI direct value in a compact form it presents complete public key data */ if (info->direct.spki.value && info->direct.spki.len) { sc_log(ctx, "Using direct SPKI value, tag 0x%X", *(info->direct.spki.value)); r = sc_pkcs15_pubkey_from_spki_sequence(ctx, info->direct.spki.value, info->direct.spki.len, &pubkey); LOG_TEST_GOTO_ERR(ctx, r, "Failed to decode 'SPKI' direct value"); } else if (info->direct.raw.value && info->direct.raw.len) { sc_log(ctx, "Using direct RAW value"); r = sc_pkcs15_decode_pubkey(ctx, pubkey, info->direct.raw.value, info->direct.raw.len); LOG_TEST_GOTO_ERR(ctx, r, "Failed to decode 'RAW' direct value"); sc_log(ctx, "TODO: for EC keys 'raw' data needs to be completed with referenced algorithm from TokenInfo"); } else if (obj->content.value && obj->content.len) { sc_log(ctx, "Using object content"); r = sc_pkcs15_decode_pubkey(ctx, pubkey, obj->content.value, obj->content.len); LOG_TEST_GOTO_ERR(ctx, r, "Failed to decode object content value"); sc_log(ctx, "TODO: for EC keys 'raw' data needs to be completed with referenced algorithm from TokenInfo"); } 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, (unsigned)info->modulus_length, &data, &len); LOG_TEST_GOTO_ERR(ctx, r, "Card specific 'read-public' procedure failed."); r = sc_pkcs15_decode_pubkey(ctx, pubkey, data, len); LOG_TEST_GOTO_ERR(ctx, r, "Decode public key error"); } else if (info->path.len) { sc_log(ctx, "Read from EF and decode"); 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 || algorithm == SC_ALGORITHM_EDDSA || algorithm == SC_ALGORITHM_XEDDSA) && *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); LOG_TEST_GOTO_ERR(ctx, r, "Decode public key error"); } else { r = SC_ERROR_NOT_IMPLEMENTED; LOG_TEST_GOTO_ERR(ctx, r, "No way to get public key"); } err: if (r) { sc_pkcs15_free_pubkey(pubkey); } else *out = pubkey; free(data); LOG_FUNC_RETURN(ctx, r); } static int sc_pkcs15_dup_bignum (struct sc_pkcs15_bignum *dst, struct sc_pkcs15_bignum *src) { if (!dst || !src) { return SC_ERROR_INVALID_ARGUMENTS; } if (src->data && src->len) { dst->data = calloc(1, src->len); if (!dst->data) return SC_ERROR_OUT_OF_MEMORY; memcpy(dst->data, src->data, src->len); dst->len = src->len; } return 0; } int sc_pkcs15_pubkey_from_prvkey(struct sc_context *ctx, struct sc_pkcs15_prkey *prvkey, struct sc_pkcs15_pubkey **out) { struct sc_pkcs15_pubkey *pubkey = NULL; int rv = SC_SUCCESS; if (!prvkey || !out) { return SC_ERROR_INVALID_ARGUMENTS; } *out = NULL; pubkey = calloc(1, sizeof(struct sc_pkcs15_pubkey)); if (!pubkey) return SC_ERROR_OUT_OF_MEMORY; pubkey->algorithm = prvkey->algorithm; switch (prvkey->algorithm) { case SC_ALGORITHM_RSA: rv = sc_pkcs15_dup_bignum(&pubkey->u.rsa.modulus, &prvkey->u.rsa.modulus); if (!rv) rv = sc_pkcs15_dup_bignum(&pubkey->u.rsa.exponent, &prvkey->u.rsa.exponent); break; case SC_ALGORITHM_GOSTR3410: break; case SC_ALGORITHM_EC: pubkey->u.ec.ecpointQ.value = malloc(prvkey->u.ec.ecpointQ.len); if (!pubkey->u.ec.ecpointQ.value) { sc_pkcs15_free_pubkey(pubkey); LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); } memcpy(pubkey->u.ec.ecpointQ.value, prvkey->u.ec.ecpointQ.value, prvkey->u.ec.ecpointQ.len); pubkey->u.ec.ecpointQ.len = prvkey->u.ec.ecpointQ.len; break; case SC_ALGORITHM_EDDSA: case SC_ALGORITHM_XEDDSA: /* Copy pubkey */ if (prvkey->u.eddsa.pubkey.value == NULL || prvkey->u.eddsa.pubkey.len <= 0) { sc_pkcs15_free_pubkey(pubkey); LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); } pubkey->u.eddsa.pubkey.value = malloc(prvkey->u.eddsa.pubkey.len); if (!pubkey->u.eddsa.pubkey.value) { sc_pkcs15_free_pubkey(pubkey); LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); } memcpy(pubkey->u.eddsa.pubkey.value, prvkey->u.eddsa.pubkey.value, prvkey->u.eddsa.pubkey.len); pubkey->u.eddsa.pubkey.len = prvkey->u.eddsa.pubkey.len; break; default: sc_log(ctx, "Unsupported private key algorithm"); rv = SC_ERROR_NOT_SUPPORTED; } if (rv) sc_pkcs15_free_pubkey(pubkey); else *out = pubkey; return rv; } int sc_pkcs15_dup_pubkey(struct sc_context *ctx, struct sc_pkcs15_pubkey *key, struct sc_pkcs15_pubkey **out) { struct sc_pkcs15_pubkey *pubkey = NULL; int rv = SC_SUCCESS; u8* alg; size_t alglen; LOG_FUNC_CALLED(ctx); if (!key || !out) { return SC_ERROR_INVALID_ARGUMENTS; } *out = NULL; pubkey = calloc(1, sizeof(struct sc_pkcs15_pubkey)); if (!pubkey) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); pubkey->algorithm = key->algorithm; if (key->alg_id) { 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) { 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); } } switch (key->algorithm) { case SC_ALGORITHM_RSA: rv = sc_pkcs15_dup_bignum(&pubkey->u.rsa.modulus, &key->u.rsa.modulus); if (!rv) rv = sc_pkcs15_dup_bignum(&pubkey->u.rsa.exponent, &key->u.rsa.exponent); break; case SC_ALGORITHM_GOSTR3410: break; case SC_ALGORITHM_EC: pubkey->u.ec.ecpointQ.value = malloc(key->u.ec.ecpointQ.len); if (!pubkey->u.ec.ecpointQ.value) { rv = SC_ERROR_OUT_OF_MEMORY; break; } 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; 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; break; } 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; 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; case SC_ALGORITHM_EDDSA: case SC_ALGORITHM_XEDDSA: /* Copy pubkey */ pubkey->u.eddsa.pubkey.value = malloc(key->u.eddsa.pubkey.len); if (!pubkey->u.eddsa.pubkey.value) { rv = SC_ERROR_OUT_OF_MEMORY; break; } memcpy(pubkey->u.eddsa.pubkey.value, key->u.eddsa.pubkey.value, key->u.eddsa.pubkey.len); pubkey->u.eddsa.pubkey.len = key->u.eddsa.pubkey.len; break; default: sc_log(ctx, "Unsupported private key algorithm"); rv = SC_ERROR_NOT_SUPPORTED; } if (rv) sc_pkcs15_free_pubkey(pubkey); else *out = pubkey; LOG_FUNC_RETURN(ctx, rv); } void sc_pkcs15_erase_pubkey(struct sc_pkcs15_pubkey *key) { if (key == NULL) { return; } if (key->alg_id) { sc_asn1_clear_algorithm_id(key->alg_id); free(key->alg_id); } 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_GOSTR3410: if (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); break; case SC_ALGORITHM_EDDSA: case SC_ALGORITHM_XEDDSA: free(key->u.eddsa.pubkey.value); key->u.eddsa.pubkey.value = NULL; key->u.eddsa.pubkey.len = 0; break; } sc_mem_clear(key, sizeof(*key)); } void sc_pkcs15_free_pubkey(struct sc_pkcs15_pubkey *key) { if (!key) return; sc_pkcs15_erase_pubkey(key); free(key); } void sc_pkcs15_free_pubkey_info(sc_pkcs15_pubkey_info_t *info) { if (info) { free(info->subject.value); free(info->direct.spki.value); free(info->direct.raw.value); sc_pkcs15_free_key_params(&info->params); free(info); } } static int sc_pkcs15_read_der_file(sc_context_t *ctx, char * filename, u8 ** buf, size_t * buflen) { int r; int f = -1; size_t len, offs; 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) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); *buf = NULL; *buflen = 0; f = open(filename, O_RDONLY); if (f < 0) { r = SC_ERROR_FILE_NOT_FOUND; goto out; } 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 = sz; body = tagbuf; r = sc_asn1_read_tag(&body, len, &cla_out, &tag_out, &bodylen); if (r != SC_SUCCESS && r != SC_ERROR_ASN1_END_OF_CONTENTS) goto out; if (body == NULL) { r = SC_SUCCESS; goto out; } offs = body - tagbuf; if (offs > len || offs < 2 || offs > offs + bodylen) { r = SC_ERROR_INVALID_ASN1_OBJECT; goto out; } rbuflen = offs + bodylen; rbuf = malloc(rbuflen); if (rbuf == NULL) { r = SC_ERROR_OUT_OF_MEMORY; goto out; } memcpy(rbuf, tagbuf, len); /* copy first or only part */ if (rbuflen > len) { /* read rest of file */ sz = read(f, rbuf + len, rbuflen - len); if (sz < (ssize_t)(rbuflen - len)) { r = SC_ERROR_INVALID_ASN1_OBJECT; free (rbuf); rbuf = NULL; goto out; } } *buflen = rbuflen; *buf = rbuf; rbuf = NULL; r = (int)rbuflen; out: if (f >= 0) close(f); LOG_FUNC_RETURN(ctx, r); } /* * can be used as an SC_ASN1_CALLBACK while parsing a certificate, * or can be called from the sc_pkcs15_pubkey_from_spki_file */ int sc_pkcs15_pubkey_from_spki_fields(struct sc_context *ctx, struct sc_pkcs15_pubkey **outpubkey, unsigned char *buf, size_t buflen, int depth) { struct sc_pkcs15_pubkey *pubkey = NULL; struct sc_pkcs15_der pk = { NULL, 0 }; struct sc_algorithm_id pk_alg; struct sc_asn1_entry asn1_pkinfo[C_ASN1_PKINFO_ATTR_SIZE]; unsigned char *tmp_buf = NULL; int r; sc_log(ctx, "sc_pkcs15_pubkey_from_spki_fields() called: %p:%"SC_FORMAT_LEN_SIZE_T"u\n%s", buf, buflen, sc_dump_hex(buf, buflen)); tmp_buf = malloc(buflen); if (!tmp_buf) { r = SC_ERROR_OUT_OF_MEMORY; LOG_TEST_GOTO_ERR(ctx, r, ""); } memcpy(tmp_buf, buf, buflen); if ((*tmp_buf & SC_ASN1_TAG_CONTEXT)) *tmp_buf = SC_ASN1_TAG_CONSTRUCTED | SC_ASN1_TAG_SEQUENCE; memset(&pk_alg, 0, sizeof(pk_alg)); pubkey = calloc(1, sizeof(sc_pkcs15_pubkey_t)); if (pubkey == NULL) { r = SC_ERROR_OUT_OF_MEMORY; LOG_TEST_GOTO_ERR(ctx, r, ""); } sc_copy_asn1_entry(c_asn1_pkinfo, asn1_pkinfo); sc_format_asn1_entry(asn1_pkinfo + 0, &pk_alg, NULL, 0); sc_format_asn1_entry(asn1_pkinfo + 1, &pk.value, &pk.len, 0); r = sc_asn1_decode(ctx, asn1_pkinfo, tmp_buf, buflen, NULL, NULL); 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) { r = SC_ERROR_OUT_OF_MEMORY; LOG_TEST_GOTO_ERR(ctx, r, ""); } 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=%lu", pk_alg.algorithm); if (pk.len == 0) LOG_TEST_GOTO_ERR(ctx, SC_ERROR_INTERNAL, "Incorrect length of key"); pk.len = (pk.len + 7)/8; /* 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 */ /* * sc_pkcs15_fix_ec_parameters below will set field_length from curve. * if no alg_id->params, assume field_length is multiple of 8 */ pubkey->u.ec.params.field_length = (pk.len - 1) / 2 * 8; if (pubkey->alg_id->params) { struct sc_ec_parameters *ecp = (struct sc_ec_parameters *)pubkey->alg_id->params; pubkey->u.ec.params.der.value = malloc(ecp->der.len); if (pubkey->u.ec.params.der.value == NULL) { r = SC_ERROR_OUT_OF_MEMORY; LOG_TEST_GOTO_ERR(ctx, r, ""); } memcpy(pubkey->u.ec.params.der.value, ecp->der.value, ecp->der.len); pubkey->u.ec.params.der.len = ecp->der.len; 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 if (pk_alg.algorithm == SC_ALGORITHM_EDDSA || pk_alg.algorithm == SC_ALGORITHM_XEDDSA) { /* EDDSA/XEDDSA public key is not encapsulated into BIT STRING -- it's a BIT STRING */ pubkey->u.eddsa.pubkey.value = malloc(pk.len); memcpy(pubkey->u.eddsa.pubkey.value, pk.value, pk.len); pubkey->u.eddsa.pubkey.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 (pubkey) sc_pkcs15_free_pubkey(pubkey); if (pk.value) free(pk.value); if (tmp_buf) free(tmp_buf); LOG_FUNC_RETURN(ctx, r); } int sc_pkcs15_pubkey_from_spki_sequence(struct sc_context *ctx, const unsigned char *buf, size_t buflen, struct sc_pkcs15_pubkey ** outpubkey) { struct sc_pkcs15_pubkey * pubkey = NULL; struct sc_asn1_entry asn1_spki[] = { { "subjectPublicKeyInfo", SC_ASN1_CALLBACK, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, sc_pkcs15_pubkey_from_spki_fields, &pubkey}, { NULL, 0, 0, 0, NULL, NULL } }; int r; LOG_FUNC_CALLED(ctx); r = sc_asn1_decode(ctx, asn1_spki, buf, buflen, NULL, NULL); LOG_TEST_RET(ctx, r, "ASN.1 cannot parse subjectPublicKeyInfo"); if(outpubkey) { free(*outpubkey); *outpubkey = pubkey; } else free(pubkey); LOG_FUNC_RETURN(ctx, r); } int sc_pkcs15_pubkey_from_spki_file(struct sc_context *ctx, char * filename, struct sc_pkcs15_pubkey ** outpubkey) { int r; u8 * buf = NULL; size_t buflen = 0; LOG_FUNC_CALLED(ctx); r = sc_pkcs15_read_der_file(ctx, filename, &buf, &buflen); 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); LOG_FUNC_RETURN(ctx, r); } static struct ec_curve_info { const char *name; const char *oid_str; const char *oid_encoded; size_t size; } 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}, {"brainpoolP384r1", "1.3.36.3.3.2.8.1.1.11", "06092B240303020801010B", 384}, {"brainpoolP512r1", "1.3.36.3.3.2.8.1.1.13", "06092B240303020801010D", 512}, {"secp192k1", "1.3.132.0.31", "06052B8104001F", 192}, {"secp256k1", "1.3.132.0.10", "06052B8104000A", 256}, {"ed25519", "1.3.6.1.4.1.11591.15.1", "06092B06010401DA470F01", 255}, {"curve25519", "1.3.6.1.4.1.3029.1.5.1", "060A2B060104019755010501", 255}, {NULL, NULL, NULL, 0}, /* Do not touch this */ }; int sc_pkcs15_fix_ec_parameters(struct sc_context *ctx, struct sc_ec_parameters *ecparams) { int rv, ii; 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; sc_format_oid(&id, ec_curve_infos[ii].oid_str); sc_encode_oid (ctx, &id, &buf, &len); if (ecparams->der.len == len && !memcmp(ecparams->der.value, buf, len)) { free(buf); break; } free(buf); } /* TODO: support of explicit EC parameters form */ if (!ec_curve_infos[ii].name) LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Unsupported named curve"); sc_log(ctx, "Found known curve '%s'", ec_curve_infos[ii].name); if (!ecparams->named_curve) { ecparams->named_curve = strdup(ec_curve_infos[ii].name); if (!ecparams->named_curve) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); 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); 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++) { 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) { sc_log(ctx, "Named curve '%s' not supported", ecparams->named_curve); LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); } rv = sc_format_oid(&ecparams->id, ec_curve_infos[ii].oid_str); LOG_TEST_RET(ctx, rv, "Invalid OID format"); ecparams->field_length = ec_curve_infos[ii].size; if (!ecparams->der.value || !ecparams->der.len) { rv = sc_encode_oid (ctx, &ecparams->id, &ecparams->der.value, &ecparams->der.len); LOG_TEST_RET(ctx, rv, "Cannot encode object 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); } int sc_pkcs15_convert_pubkey(struct sc_pkcs15_pubkey *pkcs15_key, void *evp_key) { #ifdef ENABLE_OPENSSL EVP_PKEY *pk = (EVP_PKEY *)evp_key; int pk_type; pk_type = EVP_PKEY_base_id(pk); switch (pk_type) { case EVP_PKEY_RSA: { struct sc_pkcs15_pubkey_rsa *dst = &pkcs15_key->u.rsa; /* 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 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; } #if !defined(OPENSSL_NO_EC) case NID_id_GostR3410_2001: { struct sc_pkcs15_pubkey_gostr3410 *dst = &pkcs15_key->u.gostr3410; BIGNUM *X, *Y; int r = 0; #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 && 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); if (dst->xy.data) { BN_bn2bin(Y, dst->xy.data); BN_bn2bin(X, dst->xy.data + BN_num_bytes(Y)); r = sc_mem_reverse(dst->xy.data, dst->xy.len); if (!r) r = 1; pkcs15_key->algorithm = SC_ALGORITHM_GOSTR3410; } else r = -1; } 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; 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; const EC_POINT *point = NULL; int nid = 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, point, POINT_CONVERSION_UNCOMPRESSED, buf, buflen, NULL); /* get curve name */ nid = EC_GROUP_get_curve_name(grp); if(nid != 0) { 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) { dst->ecpointQ.value = malloc(buflen); if (!dst->ecpointQ.value) return SC_ERROR_OUT_OF_MEMORY; memcpy(dst->ecpointQ.value, buf, buflen); dst->ecpointQ.len = buflen; /* calculate the field length */ dst->params.field_length = (buflen - 1) / 2 * 8; } else return SC_ERROR_INCOMPATIBLE_KEY; break; } #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; } return SC_SUCCESS; #else return SC_ERROR_NOT_IMPLEMENTED; #endif } OpenSC-0.26.1/src/libopensc/pkcs15-sc-hsm.c000066400000000000000000001644661474147347300201750ustar00rootroot00000000000000/* * pkcs15-sc-hsm.c : Initialize PKCS#15 emulation * * Copyright (C) 2012 Andreas Schwier, CardContact, Minden, Germany * * 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" #include "asn1.h" #include "common/compat_strlcpy.h" #include "common/compat_strnlen.h" #include "card-sc-hsm.h" extern struct sc_aid sc_hsm_aid; void sc_hsm_set_serialnr(sc_card_t *card, char *serial); static struct ec_curve curves[] = { { { (unsigned char *) "\x2A\x86\x48\xCE\x3D\x03\x01\x01", 8}, // secp192r1 aka prime192r1 { (unsigned char *) "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 24}, { (unsigned char *) "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFC", 24}, { (unsigned char *) "\x64\x21\x05\x19\xE5\x9C\x80\xE7\x0F\xA7\xE9\xAB\x72\x24\x30\x49\xFE\xB8\xDE\xEC\xC1\x46\xB9\xB1", 24}, { (unsigned char *) "\x04\x18\x8D\xA8\x0E\xB0\x30\x90\xF6\x7C\xBF\x20\xEB\x43\xA1\x88\x00\xF4\xFF\x0A\xFD\x82\xFF\x10\x12\x07\x19\x2B\x95\xFF\xC8\xDA\x78\x63\x10\x11\xED\x6B\x24\xCD\xD5\x73\xF9\x77\xA1\x1E\x79\x48\x11", 49}, { (unsigned char *) "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x99\xDE\xF8\x36\x14\x6B\xC9\xB1\xB4\xD2\x28\x31", 24}, { (unsigned char *) "\x01", 1} }, { { (unsigned char *) "\x2A\x86\x48\xCE\x3D\x03\x01\x07", 8}, // secp256r1 aka prime256r1 { (unsigned char *) "\xFF\xFF\xFF\xFF\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 32}, { (unsigned char *) "\xFF\xFF\xFF\xFF\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFC", 32}, { (unsigned char *) "\x5A\xC6\x35\xD8\xAA\x3A\x93\xE7\xB3\xEB\xBD\x55\x76\x98\x86\xBC\x65\x1D\x06\xB0\xCC\x53\xB0\xF6\x3B\xCE\x3C\x3E\x27\xD2\x60\x4B", 32}, { (unsigned char *) "\x04\x6B\x17\xD1\xF2\xE1\x2C\x42\x47\xF8\xBC\xE6\xE5\x63\xA4\x40\xF2\x77\x03\x7D\x81\x2D\xEB\x33\xA0\xF4\xA1\x39\x45\xD8\x98\xC2\x96\x4F\xE3\x42\xE2\xFE\x1A\x7F\x9B\x8E\xE7\xEB\x4A\x7C\x0F\x9E\x16\x2B\xCE\x33\x57\x6B\x31\x5E\xCE\xCB\xB6\x40\x68\x37\xBF\x51\xF5", 65}, { (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}, { (unsigned char *) "\x6A\x91\x17\x40\x76\xB1\xE0\xE1\x9C\x39\xC0\x31\xFE\x86\x85\xC1\xCA\xE0\x40\xE5\xC6\x9A\x28\xEF", 24}, { (unsigned char *) "\x46\x9A\x28\xEF\x7C\x28\xCC\xA3\xDC\x72\x1D\x04\x4F\x44\x96\xBC\xCA\x7E\xF4\x14\x6F\xBF\x25\xC9", 24}, { (unsigned char *) "\x04\xC0\xA0\x64\x7E\xAA\xB6\xA4\x87\x53\xB0\x33\xC5\x6C\xB0\xF0\x90\x0A\x2F\x5C\x48\x53\x37\x5F\xD6\x14\xB6\x90\x86\x6A\xBD\x5B\xB8\x8B\x5F\x48\x28\xC1\x49\x00\x02\xE6\x77\x3F\xA2\xFA\x29\x9B\x8F", 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\x05", 9}, // brainpoolP224r1 { (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 *) "\x68\xA5\xE6\x2C\xA9\xCE\x6C\x1C\x29\x98\x03\xA6\xC1\x53\x0B\x51\x4E\x18\x2A\xD8\xB0\x04\x2A\x59\xCA\xD2\x9F\x43", 28}, { (unsigned char *) "\x25\x80\xF6\x3C\xCF\xE4\x41\x38\x87\x07\x13\xB1\xA9\x23\x69\xE3\x3E\x21\x35\xD2\x66\xDB\xB3\x72\x38\x6C\x40\x0B", 28}, { (unsigned char *) "\x04\x0D\x90\x29\xAD\x2C\x7E\x5C\xF4\x34\x08\x23\xB2\xA8\x7D\xC6\x8C\x9E\x4C\xE3\x17\x4C\x1E\x6E\xFD\xEE\x12\xC0\x7D\x58\xAA\x56\xF7\x72\xC0\x72\x6F\x24\xC6\xB8\x9E\x4E\xCD\xAC\x24\x35\x4B\x9E\x99\xCA\xA3\xF6\xD3\x76\x14\x02\xCD", 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\x07", 9}, // brainpoolP256r1 { (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 *) "\x7D\x5A\x09\x75\xFC\x2C\x30\x57\xEE\xF6\x75\x30\x41\x7A\xFF\xE7\xFB\x80\x55\xC1\x26\xDC\x5C\x6C\xE9\x4A\x4B\x44\xF3\x30\xB5\xD9", 32}, { (unsigned char *) "\x26\xDC\x5C\x6C\xE9\x4A\x4B\x44\xF3\x30\xB5\xD9\xBB\xD7\x7C\xBF\x95\x84\x16\x29\x5C\xF7\xE1\xCE\x6B\xCC\xDC\x18\xFF\x8C\x07\xB6", 32}, { (unsigned char *) "\x04\x8B\xD2\xAE\xB9\xCB\x7E\x57\xCB\x2C\x4B\x48\x2F\xFC\x81\xB7\xAF\xB9\xDE\x27\xE1\xE3\xBD\x23\xC2\x3A\x44\x53\xBD\x9A\xCE\x32\x62\x54\x7E\xF8\x35\xC3\xDA\xC4\xFD\x97\xF8\x46\x1A\x14\x61\x1D\xC9\xC2\x77\x45\x13\x2D\xED\x8E\x54\x5C\x1D\x54\xC7\x2F\x04\x69\x97", 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\x09", 9}, // brainpoolP320r1 { (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 *) "\x3E\xE3\x0B\x56\x8F\xBA\xB0\xF8\x83\xCC\xEB\xD4\x6D\x3F\x3B\xB8\xA2\xA7\x35\x13\xF5\xEB\x79\xDA\x66\x19\x0E\xB0\x85\xFF\xA9\xF4\x92\xF3\x75\xA9\x7D\x86\x0E\xB4", 40}, { (unsigned char *) "\x52\x08\x83\x94\x9D\xFD\xBC\x42\xD3\xAD\x19\x86\x40\x68\x8A\x6F\xE1\x3F\x41\x34\x95\x54\xB4\x9A\xCC\x31\xDC\xCD\x88\x45\x39\x81\x6F\x5E\xB4\xAC\x8F\xB1\xF1\xA6", 40}, { (unsigned char *) "\x04\x43\xBD\x7E\x9A\xFB\x53\xD8\xB8\x52\x89\xBC\xC4\x8E\xE5\xBF\xE6\xF2\x01\x37\xD1\x0A\x08\x7E\xB6\xE7\x87\x1E\x2A\x10\xA5\x99\xC7\x10\xAF\x8D\x0D\x39\xE2\x06\x11\x14\xFD\xD0\x55\x45\xEC\x1C\xC8\xAB\x40\x93\x24\x7F\x77\x27\x5E\x07\x43\xFF\xED\x11\x71\x82\xEA\xA9\xC7\x78\x77\xAA\xAC\x6A\xC7\xD3\x52\x45\xD1\x69\x2E\x8E\xE1", 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\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\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}, { (unsigned char *) "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 24}, { (unsigned char *) "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03", 24}, { (unsigned char *) "\x04\xDB\x4F\xF1\x0E\xC0\x57\xE9\xAE\x26\xB0\x7D\x02\x80\xB7\xF4\x34\x1D\xA5\xD1\xB1\xEA\xE0\x6C\x7D\x9B\x2F\x2F\x6D\x9C\x56\x28\xA7\x84\x41\x63\xD0\x15\xBE\x86\x34\x40\x82\xAA\x88\xD9\x5E\x2F\x9D", 49}, { (unsigned char *) "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE\x26\xF2\xFC\x17\x0F\x69\x46\x6A\x74\xDE\xFD\x8D", 24}, { (unsigned char *) "\x01", 1} }, { { (unsigned char *) "\x2B\x81\x04\x00\x0A", 5}, // secp256k1 { (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\xFE\xFF\xFF\xFC\x2F", 32}, { (unsigned char *) "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 32}, { (unsigned char *) "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x07", 32}, { (unsigned char *) "\x04\x79\xBE\x66\x7E\xF9\xDC\xBB\xAC\x55\xA0\x62\x95\xCE\x87\x0B\x07\x02\x9B\xFC\xDB\x2D\xCE\x28\xD9\x59\xF2\x81\x5B\x16\xF8\x17\x98\x48\x3A\xDA\x77\x26\xA3\xC4\x65\x5D\xA4\xFB\xFC\x0E\x11\x08\xA8\xFD\x17\xB4\x48\xA6\x85\x54\x19\x9C\x47\xD0\x8F\xFB\x10\xD4\xB8", 65}, { (unsigned char *) "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE\xBA\xAE\xDC\xE6\xAF\x48\xA0\x3B\xBF\xD2\x5E\x8C\xD0\x36\x41\x41", 32}, { (unsigned char *) "\x01", 1} }, { { NULL, 0}, { NULL, 0}, { NULL, 0}, { NULL, 0}, { NULL, 0}, { NULL, 0}, { NULL, 0} } }; #define C_ASN1_CVC_PUBKEY_SIZE 10 static const struct sc_asn1_entry c_asn1_cvc_pubkey[C_ASN1_CVC_PUBKEY_SIZE] = { { "publicKeyOID", SC_ASN1_OBJECT, SC_ASN1_UNI | SC_ASN1_OBJECT, 0, NULL, NULL }, { "primeOrModulus", SC_ASN1_OCTET_STRING, SC_ASN1_CTX | 1, SC_ASN1_OPTIONAL | SC_ASN1_ALLOC, NULL, NULL }, { "coefficientAorExponent", SC_ASN1_OCTET_STRING, SC_ASN1_CTX | 2, SC_ASN1_OPTIONAL | SC_ASN1_ALLOC, NULL, NULL }, { "coefficientB", SC_ASN1_OCTET_STRING, SC_ASN1_CTX | 3, SC_ASN1_OPTIONAL | SC_ASN1_ALLOC, NULL, NULL }, { "basePointG", SC_ASN1_OCTET_STRING, SC_ASN1_CTX | 4, SC_ASN1_OPTIONAL | SC_ASN1_ALLOC, NULL, NULL }, { "order", SC_ASN1_OCTET_STRING, SC_ASN1_CTX | 5, SC_ASN1_OPTIONAL | SC_ASN1_ALLOC, NULL, NULL }, { "publicPoint", SC_ASN1_OCTET_STRING, SC_ASN1_CTX | 6, SC_ASN1_OPTIONAL | SC_ASN1_ALLOC, NULL, NULL }, { "cofactor", SC_ASN1_OCTET_STRING, SC_ASN1_CTX | 7, SC_ASN1_OPTIONAL | SC_ASN1_ALLOC, NULL, NULL }, { "modulusSize", SC_ASN1_INTEGER, SC_ASN1_UNI | SC_ASN1_INTEGER, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; #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, 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 } }; #define C_ASN1_CVCERT_SIZE 3 static const struct sc_asn1_entry c_asn1_cvcert[C_ASN1_CVCERT_SIZE] = { { "certificateBody", SC_ASN1_STRUCT, SC_ASN1_CONS | SC_ASN1_APP | 0x1F4E, 0, NULL, NULL }, { "signature", SC_ASN1_OCTET_STRING, SC_ASN1_APP | 0x1F37, SC_ASN1_ALLOC, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; #define C_ASN1_CVC_SIZE 2 static const struct sc_asn1_entry c_asn1_cvc[C_ASN1_CVC_SIZE] = { { "certificate", SC_ASN1_STRUCT, SC_ASN1_CONS | SC_ASN1_APP | 0x1F21, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; #define C_ASN1_AUTHREQ_SIZE 4 static const struct sc_asn1_entry c_asn1_authreq[C_ASN1_AUTHREQ_SIZE] = { { "certificate", SC_ASN1_STRUCT, SC_ASN1_CONS | SC_ASN1_APP | 0x1F21, 0, NULL, NULL }, { "outerCAR", SC_ASN1_PRINTABLESTRING, SC_ASN1_APP | 2, 0, NULL, NULL }, { "signature", SC_ASN1_OCTET_STRING, SC_ASN1_APP | 0x1F37, SC_ASN1_ALLOC, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; #define C_ASN1_REQ_SIZE 2 static const struct sc_asn1_entry c_asn1_req[C_ASN1_REQ_SIZE] = { { "authenticatedrequest", SC_ASN1_STRUCT, SC_ASN1_CONS | SC_ASN1_APP | 7, 0, NULL, NULL }, { 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], u8 *efbin, size_t *len, int optional) { sc_path_t path; int r; sc_path_set(&path, SC_PATH_TYPE_FILE_ID, fid, 2, 0, 0); /* look this up with our AID */ 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 || !efbin || SC_SUCCESS != sc_pkcs15_read_cached_file(p15card, &path, &efbin, len)) { /* avoid re-selection of SC-HSM */ path.aid.len = 0; r = sc_select_file(p15card->card, &path, NULL); if (r < 0) { sc_log(p15card->card->ctx, "Could not select EF"); } else { r = sc_read_binary(p15card->card, 0, efbin, *len, 0); } if (r < 0) { sc_log(p15card->card->ctx, "Could not read EF"); if (!optional) { return r; } /* optional files are saved as empty files to avoid card * transactions. Parsing the file's data will reveal that they were * missing. */ *len = 0; } else { *len = r; } if (p15card->opts.use_file_cache) { /* save this with our AID */ path.aid = sc_hsm_aid; sc_pkcs15_cache_file(p15card, &path, efbin, *len); } } 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) { sc_card_t *card = p15card->card; struct sc_asn1_entry asn1_req[C_ASN1_REQ_SIZE]; struct sc_asn1_entry asn1_authreq[C_ASN1_AUTHREQ_SIZE]; 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]; unsigned int cla = 0, tag = 0; size_t taglen; 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_cvc, asn1_cvc); 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); 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_asn1_print_tags(*buf, *buflen); */ tbuf = *buf; r = sc_asn1_read_tag(&tbuf, *buflen, &cla, &tag, &taglen); LOG_TEST_RET(card->ctx, r, "Could not decode card verifiable certificate"); /* Determine if we deal with an authenticated request, plain request or certificate */ if ((cla == (SC_ASN1_TAG_APPLICATION|SC_ASN1_TAG_CONSTRUCTED)) && (tag == 7)) { r = sc_asn1_decode(card->ctx, asn1_req, *buf, *buflen, buf, buflen); } else { r = sc_asn1_decode(card->ctx, asn1_cvc, *buf, *buflen, buf, buflen); } 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. */ int sc_pkcs15emu_sc_hsm_encode_cvc(sc_pkcs15_card_t * p15card, sc_cvc_t *cvc, u8 ** buf, size_t *buflen) { sc_card_t *card = p15card->card; 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]; int r; 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); asn1_cvc_pubkey[1].flags = SC_ASN1_OPTIONAL; asn1_cvcert[1].flags = SC_ASN1_OPTIONAL; sc_format_asn1_entry(asn1_cvc_pubkey , &cvc->pukoid, NULL, 1); if (cvc->primeOrModulus && (cvc->primeOrModuluslen > 0)) { sc_format_asn1_entry(asn1_cvc_pubkey + 1, cvc->primeOrModulus, &cvc->primeOrModuluslen, 1); } sc_format_asn1_entry(asn1_cvc_pubkey + 2, cvc->coefficientAorExponent, &cvc->coefficientAorExponentlen, 1); if (cvc->coefficientB && (cvc->coefficientBlen > 0)) { sc_format_asn1_entry(asn1_cvc_pubkey + 3, cvc->coefficientB, &cvc->coefficientBlen, 1); sc_format_asn1_entry(asn1_cvc_pubkey + 4, cvc->basePointG, &cvc->basePointGlen, 1); sc_format_asn1_entry(asn1_cvc_pubkey + 5, cvc->order, &cvc->orderlen, 1); if (cvc->publicPoint && (cvc->publicPointlen > 0)) { sc_format_asn1_entry(asn1_cvc_pubkey + 6, cvc->publicPoint, &cvc->publicPointlen, 1); } sc_format_asn1_entry(asn1_cvc_pubkey + 7, cvc->cofactor, &cvc->cofactorlen, 1); } if (cvc->modulusSize > 0) { sc_format_asn1_entry(asn1_cvc_pubkey + 8, &cvc->modulusSize, NULL, 1); } sc_format_asn1_entry(asn1_cvc_body , &cvc->cpi, NULL, 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); 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)) { sc_format_asn1_entry(asn1_cvcert + 1, cvc->signature, &cvc->signatureLen, 1); } sc_format_asn1_entry(asn1_cvc , &asn1_cvcert, NULL, 1); r = sc_asn1_encode(card->ctx, asn1_cvc, buf, buflen); LOG_TEST_RET(card->ctx, r, "Could not encode card verifiable certificate"); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } int sc_pkcs15emu_sc_hsm_get_curve(struct ec_curve **curve, u8 *oid, size_t oidlen) { int i; for (i = 0; curves[i].oid.value; i++) { if ((curves[i].oid.len == oidlen) && !memcmp(curves[i].oid.value, oid, oidlen)) { *curve = &curves[i]; return SC_SUCCESS; } } return SC_ERROR_INVALID_DATA; } int sc_pkcs15emu_sc_hsm_get_curve_oid(sc_cvc_t *cvc, const struct sc_lv_data **oid) { 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)) { *oid = &curves[i].oid; return SC_SUCCESS; } } return SC_ERROR_INVALID_DATA; } static int sc_pkcs15emu_sc_hsm_get_rsa_public_key(struct sc_context *ctx, sc_cvc_t *cvc, struct sc_pkcs15_pubkey *pubkey) { pubkey->algorithm = SC_ALGORITHM_RSA; pubkey->alg_id = (struct sc_algorithm_id *)calloc(1, sizeof(struct sc_algorithm_id)); if (!pubkey->alg_id) return SC_ERROR_OUT_OF_MEMORY; pubkey->alg_id->algorithm = SC_ALGORITHM_RSA; pubkey->u.rsa.modulus.len = cvc->primeOrModuluslen; 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) { 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); return SC_SUCCESS; } static int sc_pkcs15emu_sc_hsm_get_ec_public_key(struct sc_context *ctx, sc_cvc_t *cvc, struct sc_pkcs15_pubkey *pubkey) { struct sc_ec_parameters *ecp; const struct sc_lv_data *oid; int r; pubkey->algorithm = SC_ALGORITHM_EC; r = sc_pkcs15emu_sc_hsm_get_curve_oid(cvc, &oid); if (r != SC_SUCCESS) return r; ecp = calloc(1, sizeof(struct sc_ec_parameters)); if (!ecp) return SC_ERROR_OUT_OF_MEMORY; ecp->der.len = oid->len + 2; ecp->der.value = calloc(1, ecp->der.len); if (!ecp->der.value) { free(ecp); return SC_ERROR_OUT_OF_MEMORY; } *(ecp->der.value + 0) = 0x06; *(ecp->der.value + 1) = (u8)oid->len; memcpy(ecp->der.value + 2, oid->value, oid->len); ecp->type = 1; // Named curve pubkey->alg_id = (struct sc_algorithm_id *)calloc(1, sizeof(struct sc_algorithm_id)); if (!pubkey->alg_id) { free(ecp->der.value); free(ecp); return SC_ERROR_OUT_OF_MEMORY; } pubkey->alg_id->algorithm = SC_ALGORITHM_EC; pubkey->alg_id->params = ecp; pubkey->u.ec.ecpointQ.value = malloc(cvc->publicPointlen); 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) { 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; /* FIXME: check return value? */ sc_pkcs15_fix_ec_parameters(ctx, &pubkey->u.ec.params); return SC_SUCCESS; } int sc_pkcs15emu_sc_hsm_get_public_key(struct sc_context *ctx, sc_cvc_t *cvc, struct sc_pkcs15_pubkey *pubkey) { if (cvc->publicPoint && cvc->publicPointlen) { return sc_pkcs15emu_sc_hsm_get_ec_public_key(ctx, cvc, pubkey); } else { return sc_pkcs15emu_sc_hsm_get_rsa_public_key(ctx, cvc, pubkey); } } 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; } if (cvc->primeOrModulus) { free(cvc->primeOrModulus); cvc->primeOrModulus = NULL; } if (cvc->coefficientAorExponent) { free(cvc->coefficientAorExponent); cvc->coefficientAorExponent = NULL; } if (cvc->coefficientB) { free(cvc->coefficientB); cvc->coefficientB = NULL; } if (cvc->basePointG) { free(cvc->basePointG); cvc->basePointG = NULL; } if (cvc->order) { free(cvc->order); cvc->order = NULL; } if (cvc->publicPoint) { free(cvc->publicPoint); cvc->publicPoint = NULL; } if (cvc->cofactor) { free(cvc->cofactor); cvc->cofactor = NULL; } } 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) { struct sc_context *ctx = p15card->card->ctx; sc_card_t *card = p15card->card; sc_pkcs15_pubkey_info_t pubkey_info; sc_pkcs15_object_t pubkey_obj; struct sc_pkcs15_pubkey pubkey; sc_cvc_t cvc; u8 *cvcpo; int r; cvcpo = efbin; memset(&cvc, 0, sizeof(cvc)); r = sc_pkcs15emu_sc_hsm_decode_cvc(p15card, (const u8 **)&cvcpo, &len, &cvc); LOG_TEST_RET(ctx, r, "Could decode certificate signing request"); memset(&pubkey, 0, sizeof(pubkey)); r = sc_pkcs15emu_sc_hsm_get_public_key(ctx, &cvc, &pubkey); LOG_TEST_RET(card->ctx, r, "Could not extract public key"); memset(&pubkey_info, 0, sizeof(pubkey_info)); 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"); 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"); 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"); pubkey_info.id = key_info->id; strlcpy(pubkey_obj.label, label, sizeof(pubkey_obj.label)); if (pubkey.algorithm == SC_ALGORITHM_RSA) { pubkey_info.modulus_length = pubkey.u.rsa.modulus.len << 3; pubkey_info.usage = SC_PKCS15_PRKEY_USAGE_ENCRYPT|SC_PKCS15_PRKEY_USAGE_VERIFY|SC_PKCS15_PRKEY_USAGE_WRAP; r = sc_pkcs15emu_add_rsa_pubkey(p15card, &pubkey_obj, &pubkey_info); } 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|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); sc_pkcs15_erase_pubkey(&pubkey); return SC_SUCCESS; } /* * Add a key and the key description in PKCS#15 format to the framework */ static int sc_pkcs15emu_sc_hsm_add_prkd(sc_pkcs15_card_t * p15card, u8 keyid) { sc_card_t *card = p15card->card; sc_pkcs15_cert_info_t cert_info; sc_pkcs15_object_t cert_obj; struct sc_pkcs15_object prkd; sc_pkcs15_prkey_info_t *key_info; u8 fid[2]; /* enough to hold a complete certificate */ u8 efbin[4096]; u8 *ptr; 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, "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, "Skipping optional EF.PRKD"); /* All keys require user PIN authentication */ prkd.auth_id.len = 1; prkd.auth_id.value[0] = 1; /* * Set private key flag as all keys are private anyway */ prkd.flags |= SC_PKCS15_CO_FLAG_PRIVATE; key_info = (sc_pkcs15_prkey_info_t *)prkd.data; key_info->key_reference = keyid; key_info->path.aid.len = 0; 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); } LOG_TEST_RET(card->ctx, r, "Could not add private key to framework"); /* Check if we also have a certificate for the private key */ fid[0] = EE_CERTIFICATE_PREFIX; len = sizeof efbin; r = read_file(p15card, fid, efbin, &len, 0); 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); return SC_SUCCESS; /* Ignore any errors */ } if (efbin[0] != 0x30) { free(key_info); return SC_SUCCESS; } memset(&cert_info, 0, sizeof(cert_info)); memset(&cert_obj, 0, sizeof(cert_obj)); cert_info.id = key_info->id; sc_path_set(&cert_info.path, SC_PATH_TYPE_FILE_ID, fid, 2, 0, 0); cert_info.path.count = -1; if (p15card->opts.use_file_cache) { /* look this up with our AID, which should already be cached from the * call to `read_file`. This may have the side effect that OpenSC's * caching layer re-selects our applet *if the cached file cannot be * found/used* and we may loose the authentication status. We assume * that caching works perfectly without this side effect. */ cert_info.path.aid = sc_hsm_aid; } strlcpy(cert_obj.label, prkd.label, sizeof(cert_obj.label)); r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info); free(key_info); LOG_TEST_RET(card->ctx, r, "Could not add certificate"); return SC_SUCCESS; } /* * Add a data object and description in PKCS#15 format to the framework */ static int sc_pkcs15emu_sc_hsm_add_dcod(sc_pkcs15_card_t * p15card, u8 id) { sc_card_t *card = p15card->card; sc_pkcs15_data_info_t *data_info; sc_pkcs15_object_t data_obj; u8 fid[2]; u8 efbin[512]; const u8 *ptr; size_t len; int r; fid[0] = DCOD_PREFIX; fid[1] = 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, "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 optional EF.DCOD"); data_info = (sc_pkcs15_data_info_t *)data_obj.data; r = sc_pkcs15emu_add_data_object(p15card, &data_obj, data_info); LOG_TEST_RET(card->ctx, r, "Could not add data object to framework"); return SC_SUCCESS; } /* * Add a unrelated certificate object and description in PKCS#15 format to the framework */ static int sc_pkcs15emu_sc_hsm_add_cd(sc_pkcs15_card_t * p15card, u8 id) { sc_card_t *card = p15card->card; sc_pkcs15_cert_info_t *cert_info; sc_pkcs15_object_t obj; u8 fid[2]; u8 efbin[512]; const u8 *ptr; size_t len; int r; fid[0] = CD_PREFIX; fid[1] = 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, "Skipping optional EF.CDF"); ptr = efbin; memset(&obj, 0, sizeof(obj)); r = sc_pkcs15_decode_cdf_entry(p15card, &obj, &ptr, &len); 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; r = sc_pkcs15emu_add_x509_cert(p15card, &obj, cert_info); LOG_TEST_RET(card->ctx, r, "Could not add data object to framework"); return SC_SUCCESS; } static int sc_pkcs15emu_sc_hsm_read_tokeninfo (sc_pkcs15_card_t * p15card) { sc_card_t *card = p15card->card; int r; u8 efbin[512]; size_t len; LOG_FUNC_CALLED(card->ctx); /* Read token info */ len = sizeof efbin; r = read_file(p15card, (u8 *) "\x2F\x03", efbin, &len, 1); 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, "Skipping optional EF.TokenInfo"); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } /* * Initialize PKCS#15 emulation with user PIN, private keys, certificate and data objects * */ static int sc_pkcs15emu_sc_hsm_init (sc_pkcs15_card_t * p15card) { sc_card_t *card = p15card->card; sc_hsm_private_data_t *priv = (sc_hsm_private_data_t *) card->drv_data; sc_file_t *file = NULL; sc_path_t path; u8 filelist[MAX_EXT_APDU_LENGTH]; int filelistlength; int r, i; sc_cvc_t devcert; struct sc_app_info *appinfo; struct sc_pkcs15_auth_info pin_info; struct sc_pkcs15_object pin_obj; struct sc_pin_cmd_data pindata; u8 efbin[1024]; u8 *ptr; size_t len; LOG_FUNC_CALLED(card->ctx); appinfo = calloc(1, sizeof(struct sc_app_info)); if (appinfo == NULL) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); } appinfo->aid = sc_hsm_aid; appinfo->ddo.aid = sc_hsm_aid; p15card->app = appinfo; sc_path_set(&path, SC_PATH_TYPE_DF_NAME, sc_hsm_aid.value, sc_hsm_aid.len, 0, 0); r = sc_select_file(card, &path, &file); LOG_TEST_RET(card->ctx, r, "Could not select SmartCard-HSM application"); p15card->card->version.hw_major = 24; /* JCOP 2.4.1r3 */ p15card->card->version.hw_minor = 13; 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); /* Read device certificate to determine serial number */ if (priv->EF_C_DevAut && priv->EF_C_DevAut_len) { ptr = priv->EF_C_DevAut; len = priv->EF_C_DevAut_len; } else { len = sizeof efbin; r = read_file(p15card, (u8 *) "\x2F\x02", efbin, &len, 1); 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; } memset(&devcert, 0 ,sizeof(devcert)); r = sc_pkcs15emu_sc_hsm_decode_cvc(p15card, (const u8 **)&ptr, &len, &devcert); LOG_TEST_RET(card->ctx, r, "Could not decode EF.C_DevAut"); sc_pkcs15emu_sc_hsm_read_tokeninfo(p15card); if (p15card->tokeninfo->label == NULL) { 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); } if ((p15card->tokeninfo->manufacturer_id != NULL) && !strcmp("(unknown)", p15card->tokeninfo->manufacturer_id)) { free(p15card->tokeninfo->manufacturer_id); p15card->tokeninfo->manufacturer_id = NULL; } 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) { 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); len -= 5; 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; sc_hsm_set_serialnr(card, p15card->tokeninfo->serial_number); sc_pkcs15emu_sc_hsm_free_cvc(&devcert); 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] = 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; pin_info.attrs.pin.min_length = 6; pin_info.attrs.pin.stored_length = 0; pin_info.attrs.pin.max_length = 15; pin_info.attrs.pin.pad_char = '\0'; pin_info.tries_left = 3; pin_info.max_tries = 3; pin_obj.auth_id.len = 1; pin_obj.auth_id.value[0] = 2; strlcpy(pin_obj.label, "UserPIN", sizeof(pin_obj.label)); pin_obj.flags = SC_PKCS15_CO_FLAG_PRIVATE|SC_PKCS15_CO_FLAG_MODIFIABLE; 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)); pin_info.auth_id.len = 1; 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_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; pin_info.attrs.pin.max_length = 16; pin_info.attrs.pin.pad_char = '\0'; pin_info.tries_left = 15; pin_info.max_tries = 15; 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) { sc_pkcs15_card_clear(p15card); LOG_FUNC_RETURN(card->ctx, r); } 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 { memset(&pindata, 0, sizeof(pindata)); pindata.cmd = SC_PIN_CMD_GET_INFO; pindata.pin_type = SC_AC_CHV; pindata.pin_reference = 0x85; r = sc_pin_cmd(card, &pindata, NULL); } if (r == SC_ERROR_DATA_OBJECT_NOT_FOUND) { memset(&pindata, 0, sizeof(pindata)); pindata.cmd = SC_PIN_CMD_GET_INFO; pindata.pin_type = SC_AC_CHV; pindata.pin_reference = 0x86; r = sc_pin_cmd(card, &pindata, NULL); } 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) { switch(filelist[i]) { case KEY_PREFIX: r = sc_pkcs15emu_sc_hsm_add_prkd(p15card, filelist[i + 1]); break; case DCOD_PREFIX: r = sc_pkcs15emu_sc_hsm_add_dcod(p15card, filelist[i + 1]); break; case CD_PREFIX: r = sc_pkcs15emu_sc_hsm_add_cd(p15card, filelist[i + 1]); break; } if (r != SC_SUCCESS) { sc_log(card->ctx, "Error %d adding elements to framework", r); } } LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } int sc_pkcs15emu_sc_hsm_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *aid) { 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); } OpenSC-0.26.1/src/libopensc/pkcs15-sec.c000066400000000000000000001012311474147347300175320ustar00rootroot00000000000000/* * pkcs15-sec.c: PKCS#15 cryptography functions * * Copyright (C) 2001, 2002 Juha Yrjölä * 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 * 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 "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_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 always assume an absolute path */ if (!orig_path.len && orig_path.aid.len) { /* Private key is a SDO allocated in application DF */ path = orig_path; } else if (orig_path.len == 2 && p15card->file_app != NULL) { /* Path is relative to app. DF */ path = p15card->file_app->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 (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; senv->flags |= SC_SEC_ENV_FILE_REF_PRESENT; } else { LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "invalid private key path"); } r = sc_select_file(p15card->card, &path, NULL); LOG_TEST_RET(ctx, r, "sc_select_file() failed"); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int use_key(struct sc_pkcs15_card *p15card, const struct sc_pkcs15_object *obj, sc_security_env_t *senv, int (*card_command)(sc_card_t *card, const u8 * in, size_t inlen, u8 * out, size_t outlen), const u8 * in, size_t inlen, u8 * out, size_t outlen) { int r = SC_SUCCESS; int revalidated_cached_pin = 0; 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 (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"); } } if (r == SC_SUCCESS) r = sc_set_security_env(p15card->card, senv, 0); if (r == SC_SUCCESS) r = card_command(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); sc_unlock(p15card->card); LOG_FUNC_RETURN(p15card->card->ctx, r); } static int format_senv(struct sc_pkcs15_card *p15card, const struct sc_pkcs15_object *obj, sc_security_env_t *senv_out, sc_algorithm_info_t **alg_info_out) { 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)); /* Card driver should have the access to supported algorithms from 'tokenInfo'. So that * 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 || (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) LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "This key is not native, cannot operate with it"); switch (obj->type) { case SC_PKCS15_TYPE_PRKEY_RSA: *alg_info_out = sc_card_find_rsa_alg(p15card->card, prkey->modulus_length); if (*alg_info_out == NULL) { sc_log(ctx, "Card does not support RSA with key length %"SC_FORMAT_LEN_SIZE_T"u", prkey->modulus_length); 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: *alg_info_out = sc_card_find_gostr3410_alg(p15card->card, prkey->modulus_length); if (*alg_info_out == NULL) { sc_log(ctx, "Card does not support GOSTR3410 with key length %"SC_FORMAT_LEN_SIZE_T"u", prkey->modulus_length); 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: *alg_info_out = sc_card_find_ec_alg(p15card->card, prkey->field_length, NULL); if (*alg_info_out == NULL) { sc_log(ctx, "Card does not support EC 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_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"); } senv_out->flags |= SC_SEC_ENV_ALG_PRESENT; /* optional keyReference attribute (the default value is -1) */ if (prkey->key_reference >= 0) { senv_out->key_ref_len = 1; senv_out->key_ref[0] = prkey->key_reference & 0xFF; senv_out->flags |= SC_SEC_ENV_KEY_REF_PRESENT; } 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, void *pMechanism) { 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 *prkey = (const struct sc_pkcs15_prkey_info *) obj->data; 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"); r = format_senv(p15card, obj, &senv, &alg_info); LOG_TEST_RET(ctx, r, "Could not initialize security environment"); senv.operation = SC_SEC_OPERATION_DECIPHER; 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; r = use_key(p15card, obj, &senv, sc_decipher, in, inlen, out, outlen); LOG_TEST_RET(ctx, r, "use_key() failed"); /* Strip any padding */ 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 if (pad_flags & SC_ALGORITHM_RSA_PAD_OAEP) { size_t s = r; 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"); } #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 * Since the value may be returned, and the call is expected to provide * the buffer, we used the PKCS#11 convention of outlen == 0 and out == NULL * to indicate that this is a request for the size. * In that case r = 0, and *poutlen = expected size */ 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, size_t *poutlen) { 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 *prkey = (const struct sc_pkcs15_prkey_info *) obj->data; unsigned long pad_flags = 0, sec_flags = 0; LOG_FUNC_CALLED(ctx); if (!(prkey->usage & (SC_PKCS15_PRKEY_USAGE_DERIVE))) LOG_TEST_RET(ctx, SC_ERROR_NOT_ALLOWED, "This key cannot be used for derivation"); switch (obj->type) { case SC_PKCS15_TYPE_PRKEY_EC: case SC_PKCS15_TYPE_PRKEY_XEDDSA: if (out == NULL || *poutlen < (prkey->field_length + 7) / 8) { *poutlen = (prkey->field_length + 7) / 8; r = 0; /* say no data to return */ LOG_FUNC_RETURN(ctx, r); } break; default: LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED,"Key type not supported"); } r = format_senv(p15card, obj, &senv, &alg_info); LOG_TEST_RET(ctx, r, "Could not initialize security environment"); senv.operation = SC_SEC_OPERATION_DERIVE; 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; r = use_key(p15card, obj, &senv, sc_decipher, in, inlen, out, *poutlen); LOG_TEST_RET(ctx, r, "use_key() failed"); /* 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) #define USAGE_ANY_DECIPHER (SC_PKCS15_PRKEY_USAGE_DECRYPT|\ SC_PKCS15_PRKEY_USAGE_UNWRAP) 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, 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 = NULL, *tmp; size_t modlen = 0, buflen = 0; unsigned long pad_flags = 0, sec_flags = 0; LOG_FUNC_CALLED(ctx); if (!(prkey->usage & (SC_PKCS15_PRKEY_USAGE_SIGN|SC_PKCS15_PRKEY_USAGE_SIGNRECOVER| SC_PKCS15_PRKEY_USAGE_NONREPUDIATION))) LOG_TEST_RET(ctx, SC_ERROR_NOT_ALLOWED, "This key cannot be used for signing"); r = format_senv(p15card, obj, &senv, &alg_info); LOG_TEST_RET(ctx, r, "Could not initialize security environment"); senv.operation = SC_SEC_OPERATION_SIGN; switch (obj->type) { case SC_PKCS15_TYPE_PRKEY_RSA: modlen = (prkey->modulus_length + 7) / 8; break; case SC_PKCS15_TYPE_PRKEY_GOSTR3410: modlen = (prkey->modulus_length + 7) / 8 * 2; break; case SC_PKCS15_TYPE_PRKEY_EC: case SC_PKCS15_TYPE_PRKEY_EDDSA: case SC_PKCS15_TYPE_PRKEY_XEDDSA: modlen = ((prkey->field_length +7) / 8) * 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 (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) { r = sc_mem_reverse(buf, inlen); LOG_TEST_GOTO_ERR(ctx, r, "Reverse memory error"); } tmp = buf; /* flags: the requested algo * algo_info->flags: what is supported by the card * senv.algorithm_flags: what the card will have to do */ /* if the card has SC_ALGORITHM_NEED_USAGE set, and the * key is for signing and decryption, we need to emulate signing */ 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; LOG_TEST_GOTO_ERR(ctx, r, "Unable to add padding"); r = sc_pkcs15_decipher(p15card, obj, flags, buf, modlen, out, outlen, NULL); goto err; } /* 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; } } /* 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; } } r = sc_get_encoding_flags(ctx, flags, alg_info->flags, &pad_flags, &sec_flags); if (r != SC_SUCCESS) { 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.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 = 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) { /* Add zero-padding if input is shorter than the modulus */ if (inlen < modlen) { if (modlen > buflen) { r = SC_ERROR_BUFFER_TOO_SMALL; goto err; } memmove(tmp+modlen-inlen, tmp, inlen); memset(tmp, 0, modlen-inlen); } inlen = modlen; } /* PKCS#11 MECHANISMS V2.30: 6.3.1 EC Signatures * 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 && (senv.algorithm_flags & SC_ALGORITHM_ECDSA_HASHES) == 0) { inlen = MIN(inlen, (prkey->field_length+7)/8); } r = use_key(p15card, obj, &senv, sc_compute_signature, tmp, inlen, out, outlen); 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); } OpenSC-0.26.1/src/libopensc/pkcs15-skeid.c000066400000000000000000000134121474147347300200620ustar00rootroot00000000000000/* * 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; } OpenSC-0.26.1/src/libopensc/pkcs15-skey.c000066400000000000000000000310551474147347300177410ustar00rootroot00000000000000/* * pkcs15-skey.c: PKCS #15 secret key functions * * Copyright (C) 2002 Juha Yrjölä * Copyright (C) 2011 Viktor Tarasov * * 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 "internal.h" #include "pkcs15.h" #include "pkcs11/pkcs11.h" #include "asn1.h" #include #include #include #include /* * 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 }, { "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 }, { NULL, 0, 0, 0, NULL, NULL } }; #define C_ASN1_COM_KEY_ATTR_SIZE 7 static const struct sc_asn1_entry c_asn1_com_key_attr[C_ASN1_COM_KEY_ATTR_SIZE] = { { "iD", SC_ASN1_PKCS15_ID, SC_ASN1_TAG_OCTET_STRING, 0, NULL, NULL}, { "usage", SC_ASN1_BIT_FIELD, SC_ASN1_TAG_BIT_STRING, 0, NULL, NULL}, { "native", SC_ASN1_BOOLEAN, SC_ASN1_TAG_BOOLEAN, SC_ASN1_OPTIONAL, NULL, NULL }, { "accessFlags", SC_ASN1_BIT_FIELD, SC_ASN1_TAG_BIT_STRING, SC_ASN1_OPTIONAL, NULL, NULL}, { "keyReference",SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, SC_ASN1_OPTIONAL, NULL, NULL }, { "algReference", SC_ASN1_STRUCT, SC_ASN1_CONS | SC_ASN1_CTX | 1, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; #define C_ASN1_COM_SKEY_ATTR_SIZE 2 static const struct sc_asn1_entry c_asn1_com_skey_attr[C_ASN1_COM_SKEY_ATTR_SIZE] = { { "keyLen", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, SC_ASN1_OPTIONAL, NULL, NULL}, { NULL, 0, 0, 0, NULL, NULL } }; #define C_ASN1_COM_SKEY_GENERIC_VALUE_ATTR_SIZE 2 static const struct sc_asn1_entry c_asn1_generic_skey_value_attr[C_ASN1_COM_SKEY_GENERIC_VALUE_ATTR_SIZE] = { { "path", SC_ASN1_PATH, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL}, { NULL, 0, 0, 0, NULL, NULL } }; #define C_ASN1_COM_SKEY_GENERIC_ATTR_SIZE 2 static const struct sc_asn1_entry c_asn1_generic_skey_attr[C_ASN1_COM_SKEY_GENERIC_ATTR_SIZE] = { { "secretKeyAttributes", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL}, { NULL, 0, 0, 0, NULL, NULL } }; #define C_ASN1_SKEY_CHOICE_SIZE 5 static const struct sc_asn1_entry c_asn1_skey_choice[C_ASN1_SKEY_CHOICE_SIZE] = { { "genericSecretKey", SC_ASN1_PKCS15_OBJECT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { "desKey", SC_ASN1_PKCS15_OBJECT, SC_ASN1_CTX | 2 | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { "des2Key", SC_ASN1_PKCS15_OBJECT, SC_ASN1_CTX | 3 | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { "des3Key", SC_ASN1_PKCS15_OBJECT, SC_ASN1_CTX | 4 | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; #define C_ASN1_SKEY_SIZE 2 static const struct sc_asn1_entry c_asn1_skey[C_ASN1_SKEY_SIZE] = { { "secretKey", SC_ASN1_CHOICE, 0, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; int sc_pkcs15_decode_skdf_entry(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *obj, const u8 ** buf, size_t *buflen) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_skey_info info; 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]; struct sc_asn1_entry asn1_com_skey_attr[C_ASN1_COM_SKEY_ATTR_SIZE]; struct sc_asn1_entry asn1_generic_skey_attr[C_ASN1_COM_SKEY_GENERIC_ATTR_SIZE]; struct sc_asn1_entry asn1_generic_skey_value_attr[C_ASN1_COM_SKEY_GENERIC_VALUE_ATTR_SIZE]; struct sc_asn1_entry asn1_skey_choice[C_ASN1_SKEY_CHOICE_SIZE]; struct sc_asn1_entry asn1_skey[C_ASN1_SKEY_SIZE]; struct sc_asn1_entry asn1_supported_algorithms[C_ASN1_SUPPORTED_ALGORITHMS_SIZE]; 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); sc_copy_asn1_entry(c_asn1_skey, asn1_skey); sc_copy_asn1_entry(c_asn1_skey_choice, asn1_skey_choice); sc_copy_asn1_entry(c_asn1_supported_algorithms, asn1_supported_algorithms); sc_copy_asn1_entry(c_asn1_com_key_attr, asn1_com_key_attr); sc_copy_asn1_entry(c_asn1_com_skey_attr, asn1_com_skey_attr); sc_copy_asn1_entry(c_asn1_generic_skey_attr, asn1_generic_skey_attr); sc_copy_asn1_entry(c_asn1_generic_skey_value_attr, asn1_generic_skey_value_attr); sc_format_asn1_entry(asn1_skey + 0, asn1_skey_choice, NULL, 0); sc_format_asn1_entry(asn1_skey_choice + 0, &skey_des_obj, NULL, 0); sc_format_asn1_entry(asn1_skey_choice + 1, &skey_des_obj, NULL, 0); sc_format_asn1_entry(asn1_skey_choice + 2, &skey_des_obj, NULL, 0); sc_format_asn1_entry(asn1_skey_choice + 3, &skey_des_obj, 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); for (i=0; iname; i++) sc_format_asn1_entry(asn1_supported_algorithms + i, &info.algo_refs[i], NULL, 0); sc_format_asn1_entry(asn1_com_key_attr + 5, asn1_supported_algorithms, NULL, 0); sc_format_asn1_entry(asn1_com_skey_attr + 0, &info.value_len, NULL, 0); sc_format_asn1_entry(asn1_generic_skey_attr + 0, asn1_generic_skey_value_attr, NULL, 0); sc_format_asn1_entry(asn1_generic_skey_value_attr + 0, &info.path, NULL, 0); /* 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) { 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) obj->type = SC_PKCS15_TYPE_SKEY_2DES; else if (asn1_skey_choice[3].flags & SC_ASN1_PRESENT) obj->type = SC_PKCS15_TYPE_SKEY_3DES; else LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "unsupported secret key type"); obj->data = malloc(sizeof(info)); if (obj->data == NULL) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); memcpy(obj->data, &info, sizeof(info)); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } int sc_pkcs15_encode_skdf_entry(struct sc_context *ctx, const struct sc_pkcs15_object *obj, u8 **buf, size_t *buflen) { struct sc_pkcs15_skey_info *skey = (struct sc_pkcs15_skey_info *) obj->data; int r, i; size_t usage_len = sizeof(skey->usage); size_t af_len = sizeof(skey->access_flags); struct sc_asn1_entry asn1_com_key_attr[C_ASN1_COM_KEY_ATTR_SIZE]; struct sc_asn1_entry asn1_com_skey_attr[C_ASN1_COM_SKEY_ATTR_SIZE]; struct sc_asn1_entry asn1_generic_skey_attr[C_ASN1_COM_SKEY_GENERIC_ATTR_SIZE]; struct sc_asn1_entry asn1_generic_skey_value_attr[C_ASN1_COM_SKEY_GENERIC_VALUE_ATTR_SIZE]; struct sc_asn1_entry asn1_skey_choice[C_ASN1_SKEY_CHOICE_SIZE]; struct sc_asn1_entry asn1_skey[C_ASN1_SKEY_SIZE]; struct sc_asn1_entry asn1_supported_algorithms[C_ASN1_SUPPORTED_ALGORITHMS_SIZE]; struct sc_asn1_pkcs15_object skey_obj = { (struct sc_pkcs15_object *) obj, asn1_com_key_attr, asn1_com_skey_attr, asn1_generic_skey_attr }; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_ASN1); sc_copy_asn1_entry(c_asn1_skey, asn1_skey); sc_copy_asn1_entry(c_asn1_skey_choice, asn1_skey_choice); sc_copy_asn1_entry(c_asn1_supported_algorithms, asn1_supported_algorithms); sc_copy_asn1_entry(c_asn1_com_key_attr, asn1_com_key_attr); sc_copy_asn1_entry(c_asn1_com_skey_attr, asn1_com_skey_attr); sc_copy_asn1_entry(c_asn1_generic_skey_attr, asn1_generic_skey_attr); sc_copy_asn1_entry(c_asn1_generic_skey_value_attr, asn1_generic_skey_value_attr); sc_format_asn1_entry(asn1_skey + 0, asn1_skey_choice, NULL, 1); switch (obj->type) { case SC_PKCS15_TYPE_SKEY_GENERIC: sc_format_asn1_entry(asn1_skey_choice + 0, &skey_obj, NULL, 1); break; case SC_PKCS15_TYPE_SKEY_DES: sc_format_asn1_entry(asn1_skey_choice + 1, &skey_obj, NULL, 1); break; case SC_PKCS15_TYPE_SKEY_2DES: sc_format_asn1_entry(asn1_skey_choice + 2, &skey_obj, NULL, 1); break; case SC_PKCS15_TYPE_SKEY_3DES: sc_format_asn1_entry(asn1_skey_choice + 3, &skey_obj, NULL, 1); break; default: sc_log(ctx, "Invalid secret key type: %X", obj->type); LOG_FUNC_RETURN(ctx, SC_ERROR_INTERNAL); break; } sc_format_asn1_entry(asn1_com_key_attr + 0, &skey->id, NULL, 1); sc_format_asn1_entry(asn1_com_key_attr + 1, &skey->usage, &usage_len, 1); if (skey->native == 0) sc_format_asn1_entry(asn1_com_key_attr + 2, &skey->native, NULL, 1); if (skey->access_flags) sc_format_asn1_entry(asn1_com_key_attr + 3, &skey->access_flags, &af_len, 1); if (skey->key_reference >= 0) sc_format_asn1_entry(asn1_com_key_attr + 4, &skey->key_reference, NULL, 1); for (i=0; ialgo_refs[i]; i++) sc_format_asn1_entry(asn1_supported_algorithms + i, &skey->algo_refs[i], NULL, 1); sc_format_asn1_entry(asn1_com_key_attr + 5, asn1_supported_algorithms, NULL, skey->algo_refs[0] != 0); sc_format_asn1_entry(asn1_com_skey_attr + 0, &skey->value_len, NULL, 1); sc_format_asn1_entry(asn1_generic_skey_attr + 0, asn1_generic_skey_value_attr, NULL, 1); sc_format_asn1_entry(asn1_generic_skey_value_attr + 0, &skey->path, NULL, 1); r = sc_asn1_encode(ctx, asn1_skey, buf, buflen); 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); } } OpenSC-0.26.1/src/libopensc/pkcs15-starcert.c000066400000000000000000000173761474147347300206270ustar00rootroot00000000000000/* * partial PKCS15 emulation for G&D Starcert V2.2 cards * * Copyright (C) 2004, Nils * * 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 "common/compat_strlcpy.h" #include "pkcs15.h" #include "cardctl.h" #define MANU_ID "Giesecke & Devrient GmbH" #define STARCERT "StarCertV2201" 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; #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 int get_cert_len(sc_card_t *card, sc_path_t *path) { int r; u8 buf[8]; r = sc_select_file(card, path, NULL); if (r < 0) return 0; r = sc_read_binary(card, 0, buf, sizeof(buf), 0); if (r < 0) return 0; if (buf[0] != 0x30 || buf[1] != 0x82) return 0; path->index = 0; path->count = ((buf[2] << 8) | buf[3]) + 4; return 1; } static int starcert_detect_card(sc_pkcs15_card_t *p15card) { int r; u8 buf[128]; sc_path_t path; sc_card_t *card = p15card->card; /* check if we have the correct card OS */ if (strcmp(card->name, "STARCOS")) return SC_ERROR_WRONG_CARD; /* read EF_Info file */ sc_format_path("3F00FE13", &path); r = sc_select_file(card, &path, NULL); if (r != SC_SUCCESS) return SC_ERROR_WRONG_CARD; r = sc_read_binary(card, 0, buf, 64, 0); if (r != 64) return SC_ERROR_WRONG_CARD; if (memcmp(buf + 24, STARCERT, strlen(STARCERT))) return SC_ERROR_WRONG_CARD; return SC_SUCCESS; } static int sc_pkcs15emu_starcert_init(sc_pkcs15_card_t *p15card) { const cdata certs[] = { {"DS certificate", 0, "3F00DF01C000","1", SC_PKCS15_CO_FLAG_MODIFIABLE}, {"CA certificate", 1, "3F00DF01C008","2", SC_PKCS15_CO_FLAG_MODIFIABLE}, {"KE certificate", 0, "3F00DF01C200","3", SC_PKCS15_CO_FLAG_MODIFIABLE}, {"AUT certificate",0, "3F00DF01C500","4", SC_PKCS15_CO_FLAG_MODIFIABLE}, {NULL, 0, NULL, NULL, 0} }; const pindata pins[] = { { "99", "DS pin", "3F00DF01", 0x99, SC_PKCS15_PIN_TYPE_ASCII_NUMERIC, 8, 8, 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} }; const prdata prkeys[] = { { "01", "DS key", 1024, USAGE_NONREP, "3F00DF01", 0x84, "99", SC_PKCS15_CO_FLAG_PRIVATE}, { "03", "KE key", 1024, USAGE_KE, "3F00DF01", 0x85, NULL, SC_PKCS15_CO_FLAG_PRIVATE}, { "04", "AUT key", 1024, USAGE_AUT, "3F00DF01", 0x82, NULL, SC_PKCS15_CO_FLAG_PRIVATE}, { NULL, NULL, 0, 0, NULL, 0, NULL, 0} }; int r, i; char buf[256]; sc_path_t path; sc_file_t *file = NULL; sc_card_t *card = p15card->card; sc_serial_number_t serial; /* 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; set_string(&p15card->tokeninfo->serial_number, buf); if (!p15card->tokeninfo->serial_number) return SC_ERROR_INTERNAL; /* the manufacturer ID, in this case Giesecke & Devrient GmbH */ set_string(&p15card->tokeninfo->manufacturer_id, MANU_ID); if (!p15card->tokeninfo->manufacturer_id) goto err; /* set certs */ 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); if (!get_cert_len(card, &cert_info.path)) /* skip errors */ continue; strlcpy(cert_obj.label, certs[i].label, sizeof(cert_obj.label)); cert_obj.flags = certs[i].obj_flags; r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info); if (r < 0) goto err; } /* set pins */ 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.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) goto err; } /* 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); 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) goto err; } /* select the application DF */ sc_format_path("3F00DF01", &path); r = sc_select_file(card, &path, &file); if (r != SC_SUCCESS || !file) goto err; /* set the application DF */ 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) { if (starcert_detect_card(p15card)) return SC_ERROR_WRONG_CARD; return sc_pkcs15emu_starcert_init(p15card); } OpenSC-0.26.1/src/libopensc/pkcs15-starcos-esign.c000066400000000000000000000265321474147347300215530ustar00rootroot00000000000000/** * 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); } OpenSC-0.26.1/src/libopensc/pkcs15-syn.c000066400000000000000000000375251474147347300176070ustar00rootroot00000000000000/* * pkcs15-syn.c: PKCS #15 emulation of non-pkcs15 cards * * Copyright (C) 2003 Olaf Kirch * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "common/libscdl.h" #include "internal.h" #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[] = { { "openpgp", sc_pkcs15emu_openpgp_init_ex }, { "starcert", sc_pkcs15emu_starcert_init_ex }, { "tcos", sc_pkcs15emu_tcos_init_ex }, { "itacns", sc_pkcs15emu_itacns_init_ex }, { "PIV-II", sc_pkcs15emu_piv_init_ex }, { "cac", sc_pkcs15emu_cac_init_ex }, { "idprime", sc_pkcs15emu_idprime_init_ex }, { "gemsafeV1", sc_pkcs15emu_gemsafeV1_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 }, { "coolkey", sc_pkcs15emu_coolkey_init_ex }, { "din66291", sc_pkcs15emu_din_66291_init_ex }, { "esteid2018", sc_pkcs15emu_esteid2018_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 }, { 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); 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_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: case SC_CARD_TYPE_DNIE_BLANK: case SC_CARD_TYPE_DNIE_ADMIN: 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_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: return 1; default: return 0; } } int 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; int i, r = SC_ERROR_WRONG_CARD; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); conf_block = NULL; conf_block = sc_get_conf_block(ctx, "framework", "pkcs15", 1); if (!conf_block) { /* 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); if (r == SC_SUCCESS) /* we got a hit */ goto out; } } else { /* we have a conf file => let's use it */ int builtin_enabled; 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) { /* 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); if (r == SC_SUCCESS) /* we got a hit */ goto out; } } /* search for 'emulate foo { ... }' entries in the conf file */ sc_log(ctx, "searching for 'emulate foo { ... }' blocks"); blocks = scconf_find_blocks(ctx->conf, conf_block, "emulate", NULL); sc_log(ctx, "Blocks: %p", blocks); for (i = 0; blocks && (blk = blocks[i]) != NULL; i++) { const char *name = blk->name->data; sc_log(ctx, "trying %s", name); r = parse_emu_block(p15card, aid, blk); if (r == SC_SUCCESS) { free(blocks); goto out; } } if (blocks) free(blocks); } out: if (r == SC_SUCCESS) { p15card->magic = SC_PKCS15_CARD_MAGIC; p15card->flags |= SC_PKCS15_CARD_FLAG_EMULATED; } else { if (r != SC_ERROR_WRONG_CARD) sc_log(ctx, "Failed to load card emulator: %s", sc_strerror(r)); } LOG_FUNC_RETURN(ctx, r); } static int parse_emu_block(sc_pkcs15_card_t *p15card, struct sc_aid *aid, scconf_block *conf) { sc_card_t *card = p15card->card; sc_context_t *ctx = card->ctx; void *handle = NULL; int (*init_func)(sc_pkcs15_card_t *); int (*init_func_ex)(sc_pkcs15_card_t *, struct sc_aid *); int r; const char *driver, *module_name; driver = conf->name->data; init_func = NULL; init_func_ex = NULL; module_name = scconf_get_str(conf, "module", builtin_name); if (!strcmp(module_name, "builtin")) { int i; /* This function is built into libopensc itself. * Look it up in the table of emulators */ module_name = driver; for (i = 0; builtin_emulators[i].name; i++) { if (!strcmp(builtin_emulators[i].name, module_name)) { init_func_ex = builtin_emulators[i].handler; 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; void *address; unsigned int major = 0, minor = 0, fix = 0; sc_log(ctx, "Loading %s", module_name); /* try to open dynamic library */ handle = sc_dlopen(module_name); if (!handle) { sc_log(ctx, "unable to open dynamic library '%s': %s", module_name, sc_dlerror()); return SC_ERROR_INTERNAL; } /* try to get version of the driver/api */ get_version = (const char *(*)(void)) sc_dlsym(handle, "sc_driver_version"); if (get_version) { if (3 != sscanf(get_version(), "%u.%u.%u", &major, &minor, &fix)) { sc_log(ctx, "unable to get modules version number"); sc_dlclose(handle); return SC_ERROR_INTERNAL; } } if (!get_version || (major == 0 && minor <= 9 && fix < 3)) { /* no sc_driver_version function => assume old style * init function (note: this should later give an error */ /* get the init function name */ name = scconf_get_str(conf, "function", func_name); address = sc_dlsym(handle, name); if (address) init_func = (int (*)(sc_pkcs15_card_t *)) address; } else { name = scconf_get_str(conf, "function", exfunc_name); address = sc_dlsym(handle, name); if (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); else if (init_func) r = init_func(p15card); else r = SC_ERROR_WRONG_CARD; if (r >= 0) { sc_log(card->ctx, "%s succeeded, card bound", module_name); p15card->dll_handle = handle; } else { sc_log(card->ctx, "%s failed: %s", module_name, sc_strerror(r)); /* clear pkcs15 card */ sc_pkcs15_card_clear(p15card); if (handle) sc_dlclose(handle); } return r; } static sc_pkcs15_df_t * sc_pkcs15emu_get_df(sc_pkcs15_card_t *p15card, unsigned int type) { sc_pkcs15_df_t *df; sc_file_t *file; int created = 0; while (1) { for (df = p15card->df_list; df; df = df->next) { if (df->type == type) { if (created) df->enumerated = 1; return df; } } assert(created == 0); file = sc_file_new(); if (!file) return NULL; sc_format_path("11001101", &file->path); sc_pkcs15_add_df(p15card, type, &file->path); sc_file_free(file); created++; } } int sc_pkcs15emu_add_pin_obj(sc_pkcs15_card_t *p15card, const sc_pkcs15_object_t *obj, const sc_pkcs15_auth_info_t *in_pin) { sc_pkcs15_auth_info_t pin = *in_pin; pin.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; if(!pin.auth_method) /* or SC_AC_NONE */ pin.auth_method = SC_AC_CHV; return sc_pkcs15emu_object_add(p15card, SC_PKCS15_TYPE_AUTH_PIN, obj, &pin); } int sc_pkcs15emu_add_rsa_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_RSA, obj, &key); } int sc_pkcs15emu_add_rsa_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_RSA, obj, &key); } int sc_pkcs15emu_add_ec_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_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) { 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_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) { return sc_pkcs15emu_object_add(p15card, SC_PKCS15_TYPE_CERT_X509, obj, cert); } int sc_pkcs15emu_add_data_object(sc_pkcs15_card_t *p15card, const sc_pkcs15_object_t *obj, const sc_pkcs15_data_info_t *data) { return sc_pkcs15emu_object_add(p15card, SC_PKCS15_TYPE_DATA_OBJECT, obj, data); } int sc_pkcs15emu_object_add(sc_pkcs15_card_t *p15card, unsigned int type, const sc_pkcs15_object_t *in_obj, const void *data) { sc_pkcs15_object_t *obj; 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) { LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_OUT_OF_MEMORY); } memcpy(obj, in_obj, sizeof(*obj)); obj->type = type; switch (type & SC_PKCS15_TYPE_CLASS_MASK) { case SC_PKCS15_TYPE_AUTH: df_type = SC_PKCS15_AODF; data_len = sizeof(struct sc_pkcs15_auth_info); break; case SC_PKCS15_TYPE_PRKEY: df_type = SC_PKCS15_PRKDF; data_len = sizeof(struct sc_pkcs15_prkey_info); break; case SC_PKCS15_TYPE_PUBKEY: df_type = SC_PKCS15_PUKDF; data_len = sizeof(struct sc_pkcs15_pubkey_info); break; case SC_PKCS15_TYPE_CERT: df_type = SC_PKCS15_CDF; data_len = sizeof(struct sc_pkcs15_cert_info); break; case SC_PKCS15_TYPE_DATA_OBJECT: df_type = SC_PKCS15_DODF; data_len = sizeof(struct sc_pkcs15_data_info); break; default: sc_log(p15card->card->ctx, "Unknown PKCS15 object type %d", type); free(obj); LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_INVALID_ARGUMENTS); } obj->data = calloc(1, data_len); if (obj->data == NULL) { free(obj); 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); LOG_FUNC_RETURN(p15card->card->ctx, SC_SUCCESS); } OpenSC-0.26.1/src/libopensc/pkcs15-syn.h000066400000000000000000000063261474147347300176070ustar00rootroot00000000000000/* * pkcs15-syn.c: PKCS #15 emulation of non-pkcs15 cards * * Copyright (C) 2003 Olaf Kirch * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PKCS15_SYN_H #define PKCS15_SYN_H #ifdef __cplusplus extern "C" { #endif #include #include 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_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 *); struct sc_pkcs15_emulator_handler { const char *name; int (*handler)(sc_pkcs15_card_t *, struct sc_aid *); }; #ifdef __cplusplus } #endif #endif OpenSC-0.26.1/src/libopensc/pkcs15-tccardos.c000066400000000000000000000240711474147347300205700ustar00rootroot00000000000000/* * pkcs15-tccardos.c: PKCS#15 profile for TC CardOS M4 cards * * Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "internal.h" #include "log.h" #include "pkcs15.h" #define MANU_ID "SIEMENS AG" #define TC_CARDOS_APP_DF "3F001002" #define TC_CARDOS_LABEL "TC CardOS M4" #define TC_CARDOS_SIGN 0x0020 #define TC_CARDOS_AUTH 0x0040 #define TC_CARDOS_DEC 0x0080 #define TC_CARDOS_NOPIN 0x1000 #define TC_CARDOS_LOCALPIN 0x2000 #define TC_CARDOS_GLOBALPIN 0x3000 #define TC_CARDOS_PIN_MASK 0x3000 static int read_file(struct sc_card *card, const char *file, u8 *buf, size_t *len) { int r; struct sc_path path; struct sc_file *fid = NULL; sc_format_path(file, &path); r = sc_select_file(card, &path, &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); sc_file_free(fid); if ((size_t)r < *len) return SC_ERROR_INTERNAL; return SC_SUCCESS; } static const char *get_keyholder(int fileId) { u8 tmp = fileId & 0x0f; if (tmp < 0x08) return "CH"; else if (tmp < 0x0d) return "CA"; else if (tmp == 0x0e) return "RCA"; else return "error"; } static const char *get_service(int fileId) { u8 tmp = (fileId >> 8) & 0x0f; if (tmp == 0) return "DS"; else if (tmp == 2 || tmp == 3) return "KE"; else if (tmp == 5) return "AUT"; else return "error"; } static int create_cert_obj(sc_pkcs15_card_t *p15card, int fileId) { sc_pkcs15_object_t p15obj; sc_pkcs15_cert_info_t cinfo; memset(&p15obj, 0, sizeof(p15obj)); memset(&cinfo, 0, sizeof(cinfo)); /* the certificate attributes */ cinfo.id.value[0] = (fileId >> 8) & 0xff; cinfo.id.value[1] = fileId & 0xff; cinfo.id.len = 2; cinfo.authority = fileId & 0x08 ? 1 : 0; cinfo.path.value[0] = (fileId >> 8) & 0xff; cinfo.path.value[1] = fileId & 0xff; cinfo.path.len = 2; cinfo.path.type = SC_PATH_TYPE_FILE_ID; cinfo.path.index = 0; cinfo.path.count = -1; /* compose the certificate name from the fileID */ sprintf(p15obj.label, "C.%s.%s", get_keyholder(fileId), get_service(fileId)); p15obj.flags = 0; /* XXX */ p15obj.user_consent = 0; return sc_pkcs15emu_add_x509_cert(p15card, &p15obj, &cinfo); } static int create_pkey_obj(sc_pkcs15_card_t *p15card, int cert, int key_descr, unsigned int keyId, unsigned int pinId) { sc_pkcs15_object_t p15obj; sc_pkcs15_prkey_info_t pinfo; /* init data objects */ memset(&p15obj, 0, sizeof(p15obj)); memset(&pinfo, 0, sizeof(pinfo)); /* the private key attributes */ pinfo.id.value[0] = (cert >> 8) & 0xff; pinfo.id.value[1] = cert & 0xff; pinfo.id.len = 2; pinfo.native = 1; pinfo.key_reference = (u8)keyId; pinfo.modulus_length = 1024; /* XXX */ pinfo.access_flags = SC_PKCS15_PRKEY_ACCESS_NEVEREXTRACTABLE; pinfo.usage = 0; if (key_descr & TC_CARDOS_SIGN) pinfo.usage = SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_NONREPUDIATION; if (key_descr & TC_CARDOS_AUTH) pinfo.usage |= SC_PKCS15_PRKEY_USAGE_SIGN; if (key_descr & TC_CARDOS_DEC) pinfo.usage = SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_WRAP | SC_PKCS15_PRKEY_USAGE_UNWRAP; sc_format_path(TC_CARDOS_APP_DF, &pinfo.path); pinfo.path.index = 0; pinfo.path.count = 0; /* the common object attributes */ sprintf(p15obj.label, "SK.CH.%s", get_service(cert)); if (pinId && (key_descr & TC_CARDOS_PIN_MASK)) { p15obj.auth_id.value[0] = (u8)pinId; p15obj.auth_id.len = 1; } p15obj.flags = SC_PKCS15_CO_FLAG_PRIVATE; p15obj.user_consent = 0; p15obj.type = SC_PKCS15_TYPE_PRKEY_RSA; return sc_pkcs15emu_add_rsa_prkey(p15card, &p15obj, &pinfo); } static int create_pin_obj(sc_pkcs15_card_t *p15card, int cert, int key_descr, unsigned int pinId) { sc_pkcs15_object_t p15obj; sc_pkcs15_auth_info_t ainfo; /* init data objects */ memset(&p15obj, 0, sizeof(p15obj)); memset(&ainfo, 0, sizeof(ainfo)); /* the authentication object attributes */ ainfo.auth_id.value[0] = (u8)pinId; ainfo.auth_id.len = 1; ainfo.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; ainfo.attrs.pin.reference = (u8)pinId; ainfo.attrs.pin.flags = SC_PKCS15_PIN_FLAG_EXCHANGE_REF_DATA; if ((key_descr & TC_CARDOS_PIN_MASK) == TC_CARDOS_LOCALPIN) ainfo.attrs.pin.flags |= SC_PKCS15_PIN_FLAG_LOCAL; ainfo.attrs.pin.type = SC_PKCS15_PIN_TYPE_BCD; /* XXX */ ainfo.attrs.pin.min_length = 6; /* XXX */ ainfo.attrs.pin.stored_length = 8; /* XXX */ ainfo.attrs.pin.max_length = 8; ainfo.attrs.pin.pad_char = 0; ainfo.tries_left = 3; /* XXX */ ainfo.logged_in = SC_PIN_STATE_UNKNOWN; sc_format_path(TC_CARDOS_APP_DF, &ainfo.path); ainfo.path.index = 0; ainfo.path.count = 0; /* the common object attributes */ sprintf(p15obj.label, "PIN.CH.%s", get_service(cert)); p15obj.flags = SC_PKCS15_CO_FLAG_PRIVATE; p15obj.user_consent = 0; p15obj.type = SC_PKCS15_TYPE_AUTH_PIN; return sc_pkcs15emu_add_pin_obj(p15card, &p15obj, &ainfo); } #define MAX_INFO1_SIZE 256 #define MAX_INFO2_SIZE 256 static int parse_EF_CardInfo(sc_pkcs15_card_t *p15card) { int r; u8 info1[MAX_INFO1_SIZE]; size_t info1_len = MAX_INFO1_SIZE; u8 info2[MAX_INFO2_SIZE]; size_t info2_len = MAX_INFO2_SIZE; u8 *p1, *p2; size_t i; unsigned int key_num; struct sc_context *ctx = p15card->card->ctx; size_t offset; /* read EF_CardInfo1 */ r = read_file(p15card->card, "3F001003b200", info1, &info1_len); if (r != SC_SUCCESS || info1_len < 4) return SC_ERROR_WRONG_CARD; /* read EF_CardInfo2 */ r = read_file(p15card->card, "3F001003b201", info2, &info2_len); if (r != SC_SUCCESS) return SC_ERROR_WRONG_CARD; /* get the number of private keys */ key_num = ((unsigned int) info1[info1_len-1]) | (((unsigned int) info1[info1_len-2]) << 8) | (((unsigned int) info1[info1_len-3]) << 16) | (((unsigned int) info1[info1_len-4]) << 24); sc_log(ctx, "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 >= info1_len) return SC_ERROR_INVALID_DATA; p1 = info1 + offset; p2 = info2; /* This is the minimum amount of data expected by the following code without * overunning the buffer without additional condition for cert_count == 4 */ if (info2_len < key_num * 14) return SC_ERROR_INVALID_DATA; for (i=0; icard; sc_serial_number_t iccsn; iccsn.len = sizeof iccsn.value; /* check if we have the correct card OS */ if (strcmp(card->name, "CardOS M4")) return SC_ERROR_WRONG_CARD; /* create pkcs15 objects */ r = parse_EF_CardInfo(p15card); if (r != SC_SUCCESS) return r; /* set card label */ set_string(&p15card->tokeninfo->label, TC_CARDOS_LABEL); if (p15card->tokeninfo->label == NULL) return SC_ERROR_OUT_OF_MEMORY; /* set the manufacturer ID */ 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 = 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) { r = SC_ERROR_INTERNAL; goto err; } /* set the application DF */ 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) { return sc_pkcs15_tccardos_init_func(p15card); } OpenSC-0.26.1/src/libopensc/pkcs15-tcos.c000066400000000000000000000454261474147347300177450ustar00rootroot00000000000000/* * PKCS15 emulation layer for TCOS based preformatted cards * * Copyright (C) 2011, Peter Koch * * 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 "common/compat_strlcat.h" #include "internal.h" #include "pkcs15.h" #include "cardctl.h" #include "log.h" static int insert_cert( sc_pkcs15_card_t *p15card, const char *path, unsigned char id, int writable, const char *label ){ sc_card_t *card=p15card->card; sc_context_t *ctx=p15card->card->ctx; 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)); cert_info.id.len = 1; cert_info.id.value[0] = id; cert_info.authority = 0; sc_format_path(path, &cert_info.path); memset(&cert_obj, 0, sizeof(cert_obj)); 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_log(ctx, "Select(%s) failed", path); return 1; } r = sc_read_binary(card, 0, cert, sizeof(cert), 0); if (r <= 0) { sc_log(ctx, "ReadBinary(%s) failed\n", path); return 2; } 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_len >= 5 && (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 { cert_info.path.index=0; 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_log(ctx, "sc_pkcs15emu_add_x509_cert(%s) failed", path); return 4; } sc_log(ctx, "%s: OK, Index=%d, Count=%d", path, cert_info.path.index, cert_info.path.count); return 0; } static int insert_key( sc_pkcs15_card_t *p15card, const char *path, unsigned char id, unsigned char key_reference, int key_length, unsigned char auth_id, const char *label ) { 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; int r, can_sign, can_crypt; memset(&prkey_info, 0, sizeof(prkey_info)); prkey_info.id.len = 1; prkey_info.id.value[0] = id; prkey_info.native = 1; prkey_info.key_reference = key_reference; prkey_info.modulus_length = key_length; sc_format_path(path, &prkey_info.path); memset(&prkey_obj, 0, sizeof(prkey_obj)); strlcpy(prkey_obj.label, label, sizeof(prkey_obj.label)); prkey_obj.flags = SC_PKCS15_CO_FLAG_PRIVATE; 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) { unsigned char buf[256]; 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_log(ctx, "Select(%s) failed", sc_print_path(&prkey_info.path)); return 1; } sc_log(ctx, "Searching for Key-Ref %02X", key_reference); while ((r = sc_read_record(card, ++rec_no, 0, buf, sizeof(buf), SC_RECORD_BY_REC_NR)) > 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 (r <= 0) { sc_log(ctx, "No EF_KEYD-Record found"); return 1; } for (i = 0; i + 1 < r; i += 2 + buf[i + 1]) { if (buf[i] == 0xB6) can_sign++; if (buf[i] == 0xB8) can_crypt++; } } else { if (sc_select_file(card, &prkey_info.path, &f) != SC_SUCCESS || !f->prop_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; 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_log(ctx, "sc_pkcs15emu_add_rsa_prkey(%s) failed\n", path); return 4; } sc_log(ctx, "%s: OK%s%s\n", path, can_sign ? ", Sign" : "", can_crypt ? ", Crypt" : ""); return 0; } static int insert_pin( sc_pkcs15_card_t *p15card, const char *path, unsigned char id, unsigned char auth_id, unsigned char pin_reference, int min_length, const char *label, int pin_flags ){ sc_card_t *card=p15card->card; sc_context_t *ctx=p15card->card->ctx; sc_file_t *f = NULL; struct sc_pkcs15_auth_info pin_info; struct sc_pkcs15_object pin_obj; int r; memset(&pin_info, 0, sizeof(pin_info)); pin_info.auth_id.len = 1; pin_info.auth_id.value[0] = id; pin_info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; pin_info.attrs.pin.reference = pin_reference; pin_info.attrs.pin.flags = pin_flags; pin_info.attrs.pin.type = SC_PKCS15_PIN_TYPE_ASCII_NUMERIC; pin_info.attrs.pin.min_length = min_length; pin_info.attrs.pin.stored_length = 16; pin_info.attrs.pin.max_length = 16; pin_info.attrs.pin.pad_char = '\0'; pin_info.logged_in = SC_PIN_STATE_UNKNOWN; sc_format_path(path, &pin_info.path); memset(&pin_obj, 0, sizeof(pin_obj)); strlcpy(pin_obj.label, label, sizeof(pin_obj.label)); pin_obj.flags = SC_PKCS15_CO_FLAG_MODIFIABLE | SC_PKCS15_CO_FLAG_PRIVATE; 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) { unsigned char buf[256]; int i, rec_no=0; 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_log(ctx, "Select(%s) failed", sc_print_path(&pin_info.path)); return 1; } sc_log(ctx, "Searching for PIN-Ref %02X", pin_reference); while ((r = sc_read_record(card, ++rec_no, 0, buf, sizeof(buf), SC_RECORD_BY_REC_NR)) > 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 (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 || !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]; sc_file_free(f); } r=sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info); if(r!=SC_SUCCESS){ sc_log(ctx, "sc_pkcs15emu_add_pin_obj(%s) failed\n", path); return 4; } sc_log(ctx, "%s: OK, FBZ=%d\n", path, pin_info.tries_left); return 0; } static char *dirpath(char *dir, const char *path){ static char buf[SC_MAX_PATH_STRING_SIZE]; strlcpy(buf,dir,sizeof buf); strlcat(buf,path,sizeof buf); return buf; } static int detect_netkey( sc_pkcs15_card_t *p15card ){ sc_card_t *card=p15card->card; sc_path_t p; sc_file_t *f; int keylen; char dir[10]; const char *c_auth; /* NKS-Applikation ? */ memset(&p, 0, sizeof(sc_path_t)); p.type=SC_PATH_TYPE_DF_NAME; memcpy(p.value, "\xD2\x76\x00\x00\x03\x01\x02", p.len=7); if (sc_select_file(card,&p,&f)!=SC_SUCCESS) return 1; sprintf(dir,"%04X", f->id); sc_file_free(f); 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"; insert_cert(p15card, dirpath(dir,"4331"), 0x45, 1, "Signatur Zertifikat 1"); insert_cert(p15card, dirpath(dir,"4332"), 0x45, 1, "Signatur Zertifikat 2"); insert_cert(p15card, dirpath(dir,"C000"), 0x45, 0, "Telesec Signatur Zertifikat"); insert_cert(p15card, dirpath(dir,"43B1"), 0x46, 1, "Verschluesselungs Zertifikat 1"); insert_cert(p15card, dirpath(dir,"43B2"), 0x46, 1, "Verschluesselungs Zertifikat 2"); insert_cert(p15card, dirpath(dir,"C200"), 0x46, 0, "Telesec Verschluesselungs Zertifikat"); insert_cert(p15card, dirpath(dir,"4371"), 0x47, 1, "Authentifizierungs Zertifikat 1"); insert_cert(p15card, dirpath(dir,"4372"), 0x47, 1, "Authentifizierungs Zertifikat 2"); insert_cert(p15card, dirpath(dir,c_auth), 0x47, 0, "Telesec Authentifizierungs Zertifikat"); insert_cert(p15card, dirpath(dir,"C201"), 0x48, 0, "Telesec 1024bit Zertifikat"); insert_key(p15card, dirpath(dir,"5331"), 0x45, 0x80, keylen, 4, "Signatur Schluessel"); insert_key(p15card, dirpath(dir,"53B1"), 0x46, 0x81, keylen, 3, "Verschluesselungs Schluessel"); insert_key(p15card, dirpath(dir,"5371"), 0x47, 0x82, keylen, 3, "Authentifizierungs Schluessel"); insert_key(p15card, dirpath(dir,"0000"), 0x48, 0x83, 1024, 3, "1024bit Schluessel"); insert_pin(p15card, "5000", 1, 2, 0x00, 6, "PIN", SC_PKCS15_PIN_FLAG_CASE_SENSITIVE | SC_PKCS15_PIN_FLAG_INITIALIZED ); insert_pin(p15card, "5001", 2, 0, 0x01, 8, "PUK", SC_PKCS15_PIN_FLAG_CASE_SENSITIVE | SC_PKCS15_PIN_FLAG_INITIALIZED | SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN | SC_PKCS15_PIN_FLAG_SO_PIN ); if(card->type==SC_CARD_TYPE_TCOS_V3){ insert_pin(p15card, dirpath(dir,"0000"), 3, 1, 0x83, 6, "NetKey PIN2", SC_PKCS15_PIN_FLAG_CASE_SENSITIVE | SC_PKCS15_PIN_FLAG_LOCAL | SC_PKCS15_PIN_FLAG_INITIALIZED ); } else { insert_pin(p15card, dirpath(dir,"5080"), 3, 1, 0x80, 6, "NetKey PIN0", SC_PKCS15_PIN_FLAG_CASE_SENSITIVE | SC_PKCS15_PIN_FLAG_LOCAL | SC_PKCS15_PIN_FLAG_INITIALIZED ); } insert_pin(p15card, dirpath(dir,"5081"), 4, 1, 0x81, 6, "NetKey PIN1", SC_PKCS15_PIN_FLAG_CASE_SENSITIVE | SC_PKCS15_PIN_FLAG_LOCAL | SC_PKCS15_PIN_FLAG_INITIALIZED ); /* SigG-Applikation */ p.len=7; p.type=SC_PATH_TYPE_DF_NAME; memcpy(p.value, "\xD2\x76\x00\x00\x66\x01", p.len=6); if (sc_select_file(card,&p,&f)==SC_SUCCESS){ sprintf(dir,"%04X", f->id); sc_file_free(f); 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 { insert_key(p15card, dirpath(dir,"5331"), 0x49, 0x80, 1024, 5, "SigG Schluessel"); } insert_pin(p15card, dirpath(dir,"5081"), 5, 0, 0x81, 6, "SigG PIN", SC_PKCS15_PIN_FLAG_CASE_SENSITIVE | SC_PKCS15_PIN_FLAG_LOCAL | SC_PKCS15_PIN_FLAG_INITIALIZED ); if(card->type==SC_CARD_TYPE_TCOS_V3){ insert_pin(p15card, dirpath(dir,"0000"), 6, 0, 0x83, 8, "SigG PIN2", SC_PKCS15_PIN_FLAG_CASE_SENSITIVE | SC_PKCS15_PIN_FLAG_LOCAL | SC_PKCS15_PIN_FLAG_INITIALIZED ); } } return 0; } static int detect_idkey( sc_pkcs15_card_t *p15card ){ sc_card_t *card=p15card->card; sc_path_t p; /* TCKEY-Applikation ? */ memset(&p, 0, sizeof(sc_path_t)); p.type=SC_PATH_TYPE_DF_NAME; memcpy(p.value, "\xD2\x76\x00\x00\x03\x0C\x01", p.len=7); if (sc_select_file(card,&p,NULL)!=SC_SUCCESS) return 1; 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", 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"); insert_key(p15card, "DF074E05", 0x47, 0x86, 2048, 1, "IDKey3"); 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 ); insert_pin(p15card, "5001", 2, 0, 0x01, 8, "PUK", SC_PKCS15_PIN_FLAG_CASE_SENSITIVE | SC_PKCS15_PIN_FLAG_INITIALIZED | SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN | SC_PKCS15_PIN_FLAG_SO_PIN ); return 0; } static int detect_signtrust( sc_pkcs15_card_t *p15card ){ if(insert_cert(p15card,"8000DF01C000", 0x45, 1, "Signatur Zertifikat")) return 1; 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"); insert_key(p15card,"8000DF015331", 0x45, 0x80, 1024, 1, "Signatur Schluessel"); insert_key(p15card,"800082008210", 0x46, 0x80, 1024, 2, "Verschluesselungs Schluessel"); insert_key(p15card,"800083008310", 0x47, 0x80, 1024, 3, "Authentifizierungs Schluessel"); insert_pin(p15card,"8000DF010000", 1, 0, 0x81, 6, "Signatur PIN", SC_PKCS15_PIN_FLAG_CASE_SENSITIVE | SC_PKCS15_PIN_FLAG_LOCAL | SC_PKCS15_PIN_FLAG_INITIALIZED ); insert_pin(p15card,"800082000040", 2, 0, 0x81, 6, "Verschluesselungs PIN", SC_PKCS15_PIN_FLAG_CASE_SENSITIVE | SC_PKCS15_PIN_FLAG_LOCAL | SC_PKCS15_PIN_FLAG_INITIALIZED ); insert_pin(p15card,"800083000040", 3, 0, 0x81, 6, "Authentifizierungs PIN", SC_PKCS15_PIN_FLAG_CASE_SENSITIVE | SC_PKCS15_PIN_FLAG_LOCAL | SC_PKCS15_PIN_FLAG_INITIALIZED ); return 0; } static int detect_datev( sc_pkcs15_card_t *p15card ){ if(insert_cert(p15card,"3000C500", 0x45, 0, "Signatur Zertifikat")) return 1; 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"); insert_key(p15card,"30005371", 0x45, 0x82, 1024, 1, "Signatur Schluessel"); insert_key(p15card,"DF0253B1", 0x46, 0x81, 1024, 1, "Verschluesselungs Schluessel"); insert_key(p15card,"DF025371", 0x47, 0x82, 1024, 1, "Authentifizierungs Schluessel"); insert_pin(p15card,"5001", 1, 0, 0x01, 6, "PIN", SC_PKCS15_PIN_FLAG_CASE_SENSITIVE | SC_PKCS15_PIN_FLAG_INITIALIZED ); return 0; } static int detect_unicard( sc_pkcs15_card_t *p15card ){ if(!insert_cert(p15card,"41004352", 0x45, 1, "Zertifikat 1")){ 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"); insert_key(p15card,"41005103", 0x45, 0x83, 1024, 1, "Schluessel 1"); insert_key(p15card,"41005104", 0x46, 0x84, 1024, 1, "Schluessel 2"); insert_key(p15card,"41005105", 0x47, 0x85, 1024, 1, "Schluessel 3"); } else if(!insert_cert(p15card,"41014352", 0x45, 1, "Zertifikat 1")){ 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"); insert_key(p15card,"41015103", 0x45, 0x83, 1024, 1, "Schluessel 1"); insert_key(p15card,"41015104", 0x46, 0x84, 1024, 1, "Schluessel 2"); insert_key(p15card,"41015105", 0x47, 0x85, 1024, 1, "Schluessel 3"); } else return 1; insert_pin(p15card,"5000", 1, 2, 0x00, 6, "PIN", SC_PKCS15_PIN_FLAG_CASE_SENSITIVE | SC_PKCS15_PIN_FLAG_INITIALIZED ); insert_pin(p15card,"5008", 2, 0, 0x01, 8, "PUK", SC_PKCS15_PIN_FLAG_CASE_SENSITIVE | SC_PKCS15_PIN_FLAG_INITIALIZED | SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN | SC_PKCS15_PIN_FLAG_SO_PIN ); return 0; } int sc_pkcs15emu_tcos_init_ex( sc_pkcs15_card_t *p15card, 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 r; /* check if we have the correct card OS unless SC_PKCS15EMU_FLAGS_NO_CHECK */ 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_log(ctx, "unable to get ICCSN"); return SC_ERROR_WRONG_CARD; } 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'; set_string(&p15card->tokeninfo->serial_number, serial); if(!detect_netkey(p15card)) return SC_SUCCESS; if(!detect_idkey(p15card)) return SC_SUCCESS; if(!detect_unicard(p15card)) return SC_SUCCESS; if(!detect_signtrust(p15card)) return SC_SUCCESS; if(!detect_datev(p15card)) return SC_SUCCESS; sc_pkcs15_card_clear(p15card); return SC_ERROR_INTERNAL; } OpenSC-0.26.1/src/libopensc/pkcs15.c000066400000000000000000002611151474147347300167720ustar00rootroot00000000000000/* * pkcs15.c: PKCS #15 general functions * * Copyright (C) 2001, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include "cardctl.h" #include "internal.h" #include "pkcs15.h" #include "asn1.h" #include "common/libscdl.h" #ifdef ENABLE_OPENSSL #include #endif #ifdef HAVE_SYS_TIME_H #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 } }; 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_CHOICE, 0, 0, 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 } }; 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 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 }, { "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 }, { NULL, 0, 0, 0, NULL, NULL } }; #define C_ASN1_LAST_UPDATE_SIZE 3 static const struct sc_asn1_entry c_asn1_last_update[C_ASN1_LAST_UPDATE_SIZE] = { { "generalizedTime", SC_ASN1_GENERALIZEDTIME, SC_ASN1_TAG_GENERALIZEDTIME, SC_ASN1_OPTIONAL, NULL, NULL }, { "referencedTime", SC_ASN1_PATH, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; #define C_ASN1_PROFILE_INDICATION_SIZE 3 static const struct sc_asn1_entry c_asn1_profile_indication[C_ASN1_PROFILE_INDICATION_SIZE] = { { "profileOID", SC_ASN1_OBJECT, SC_ASN1_TAG_OBJECT, SC_ASN1_OPTIONAL, NULL, NULL }, { "profileName", SC_ASN1_UTF8STRING, SC_ASN1_TAG_UTF8STRING, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; #define C_ASN1_TOKI_ATTRS_SIZE 15 static const struct sc_asn1_entry c_asn1_toki_attrs[C_ASN1_TOKI_ATTRS_SIZE] = { { "version", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, { "serialNumber", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_OCTET_STRING, SC_ASN1_OPTIONAL, NULL, NULL }, { "manufacturerID", SC_ASN1_UTF8STRING, SC_ASN1_TAG_UTF8STRING, SC_ASN1_OPTIONAL, NULL, NULL }, { "label", SC_ASN1_UTF8STRING, SC_ASN1_CTX | 0, SC_ASN1_OPTIONAL, NULL, NULL }, /* XXX the Taiwanese ID card erroneously uses explicit tagging */ { "label-tw", SC_ASN1_STRUCT, SC_ASN1_CTX | 0 | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { "tokenflags", SC_ASN1_BIT_FIELD, SC_ASN1_TAG_BIT_STRING, 0, NULL, NULL }, { "seInfo", SC_ASN1_SE_INFO, SC_ASN1_CONS | SC_ASN1_TAG_SEQUENCE, SC_ASN1_OPTIONAL, NULL, NULL }, { "recordInfo", SC_ASN1_STRUCT, SC_ASN1_CONS | SC_ASN1_CTX | 1, SC_ASN1_OPTIONAL, NULL, NULL }, { "supportedAlgorithms", SC_ASN1_STRUCT, SC_ASN1_CONS | SC_ASN1_CTX | 2, SC_ASN1_OPTIONAL, NULL, NULL }, { "issuerId", SC_ASN1_UTF8STRING, SC_ASN1_CTX | 3, SC_ASN1_OPTIONAL, NULL, NULL }, { "holderId", SC_ASN1_UTF8STRING, SC_ASN1_CTX | 4, SC_ASN1_OPTIONAL, NULL, NULL }, { "lastUpdate", SC_ASN1_STRUCT, SC_ASN1_CONS | SC_ASN1_CTX | 5, SC_ASN1_OPTIONAL, NULL, NULL }, { "preferredLanguage", SC_ASN1_PRINTABLESTRING, SC_ASN1_TAG_PRINTABLESTRING, SC_ASN1_OPTIONAL, NULL, NULL }, { "profileIndication", SC_ASN1_STRUCT, SC_ASN1_CONS | SC_ASN1_CTX | 6, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; static const struct sc_asn1_entry c_asn1_tokeninfo[] = { { "TokenInfo", SC_ASN1_STRUCT, SC_ASN1_CONS | SC_ASN1_TAG_SEQUENCE, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; static void sc_pkcs15_free_unusedspace(struct sc_pkcs15_card *); 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) { int r; size_t ii; u8 serial[128]; size_t serial_len = sizeof(serial); u8 mnfid[SC_PKCS15_MAX_LABEL_SIZE]; size_t mnfid_len = sizeof(mnfid) - 1; u8 label[SC_PKCS15_MAX_LABEL_SIZE]; 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_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]; 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); sc_copy_asn1_entry(c_asn1_last_update, asn1_last_update); 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, 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); sc_format_asn1_entry(asn1_supported_algorithms + ii, asn1_algo_infos[ii], NULL, 0); } sc_format_asn1_entry(asn1_last_update + 0, last_update, &lupdate_len, 0); sc_format_asn1_entry(asn1_last_update + 1, &ti->last_update.path, NULL, 0); sc_format_asn1_entry(asn1_profile_indication + 0, &ti->profile_indication.oid, NULL, 0); sc_format_asn1_entry(asn1_profile_indication + 1, profile_indication, &pi_len, 0); sc_format_asn1_entry(asn1_toki_attrs + 0, &ti->version, NULL, 0); sc_format_asn1_entry(asn1_toki_attrs + 1, serial, &serial_len, 0); sc_format_asn1_entry(asn1_toki_attrs + 2, mnfid, &mnfid_len, 0); sc_format_asn1_entry(asn1_toki_attrs + 3, label, &label_len, 0); sc_format_asn1_entry(asn1_toki_attrs + 4, asn1_twlabel, NULL, 0); sc_format_asn1_entry(asn1_toki_attrs + 5, &ti->flags, &flags_len, 0); sc_format_asn1_entry(asn1_toki_attrs + 6, &ti->seInfo, &ti->num_seInfo, 0); sc_format_asn1_entry(asn1_toki_attrs + 7, NULL, NULL, 0); sc_format_asn1_entry(asn1_toki_attrs + 8, asn1_supported_algorithms, NULL, 0); sc_format_asn1_entry(asn1_toki_attrs + 9, NULL, NULL, 0); sc_format_asn1_entry(asn1_toki_attrs + 10, NULL, NULL, 0); sc_format_asn1_entry(asn1_toki_attrs + 11, asn1_last_update, NULL, 0); sc_format_asn1_entry(asn1_toki_attrs + 12, preferred_language, &lang_length, 0); sc_format_asn1_entry(asn1_toki_attrs + 13, asn1_profile_indication, NULL, 0); sc_format_asn1_entry(asn1_tokeninfo, asn1_toki_attrs, NULL, 0); r = sc_asn1_decode(ctx, asn1_tokeninfo, buf, blen, NULL, NULL); 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 && 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; sc_bin_to_hex(serial, serial_len, ti->serial_number, serial_len * 2 + 1, 0); sc_log(ctx, "TokenInfo.serialNunmber '%s'", ti->serial_number); } if (ti->manufacturer_id == NULL) { if (asn1_toki_attrs[2].flags & SC_ASN1_PRESENT) ti->manufacturer_id = strdup((char *) mnfid); else ti->manufacturer_id = strdup("(unknown)"); if (ti->manufacturer_id == NULL) return SC_ERROR_OUT_OF_MEMORY; } if (ti->label == NULL) { if (asn1_toki_attrs[3].flags & SC_ASN1_PRESENT || asn1_toki_attrs[4].flags & SC_ASN1_PRESENT) ti->label = strdup((char *) label); else ti->label = strdup("(unknown)"); if (ti->label == NULL) return SC_ERROR_OUT_OF_MEMORY; } if (asn1_toki_attrs[11].flags & SC_ASN1_PRESENT) { if (asn1_last_update[0].flags & SC_ASN1_PRESENT) { sc_log(ctx, "LastUpdate.generalizedTime present"); ti->last_update.gtime = strdup((char *)last_update); if (ti->last_update.gtime == NULL) return SC_ERROR_OUT_OF_MEMORY; } else if (asn1_last_update[1].flags & SC_ASN1_PRESENT) { sc_log(ctx, "LastUpdate.referencedTime present"); } } if (asn1_toki_attrs[12].flags & SC_ASN1_PRESENT) { preferred_language[2] = 0; ti->preferred_language = strdup((char *)preferred_language); if (ti->preferred_language == NULL) return SC_ERROR_OUT_OF_MEMORY; } sc_init_oid(&ti->profile_indication.oid); if (asn1_toki_attrs[13].flags & SC_ASN1_PRESENT) { if (asn1_profile_indication[0].flags & SC_ASN1_PRESENT) { sc_log(ctx, "ProfileIndication.oid present"); } else if (asn1_profile_indication[1].flags & SC_ASN1_PRESENT) { sc_log(ctx, "ProfileIndication.name present"); ti->profile_indication.name = strdup((char *)profile_indication); if (ti->profile_indication.name == NULL) return SC_ERROR_OUT_OF_MEMORY; } } sc_log(ctx, "LastUpdate.path '%s'", sc_print_path(&ti->last_update.path)); sc_log(ctx, "ProfileIndication.name '%s'", ti->profile_indication.name); return SC_SUCCESS; } int sc_pkcs15_encode_tokeninfo(sc_context_t *ctx, sc_pkcs15_tokeninfo_t *ti, u8 **buf, size_t *buflen) { int r, ii; size_t serial_len, mnfid_len, label_len, flags_len, last_upd_len, pi_len; 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_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++) { 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, 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); sc_format_asn1_entry(asn1_supported_algorithms + ii, asn1_algo_infos[ii], NULL, 1); } sc_format_asn1_entry(asn1_toki_attrs + 0, &ti->version, NULL, 1); if (ti->serial_number != NULL) { serial_len = 0; if (strlen(ti->serial_number)/2 > sizeof(serial)) return SC_ERROR_BUFFER_TOO_SMALL; serial_len = sizeof(serial); if (sc_hex_to_bin(ti->serial_number, serial, &serial_len) < 0) return SC_ERROR_INVALID_ARGUMENTS; sc_format_asn1_entry(asn1_toki_attrs + 1, serial, &serial_len, 1); } else { sc_format_asn1_entry(asn1_toki_attrs + 1, NULL, NULL, 0); } if (ti->manufacturer_id != NULL) { mnfid_len = strlen(ti->manufacturer_id); sc_format_asn1_entry(asn1_toki_attrs + 2, ti->manufacturer_id, &mnfid_len, 1); } else { sc_format_asn1_entry(asn1_toki_attrs + 2, NULL, NULL, 0); } if (ti->label != NULL) { label_len = strlen(ti->label); sc_format_asn1_entry(asn1_toki_attrs + 3, ti->label, &label_len, 1); } else { sc_format_asn1_entry(asn1_toki_attrs + 3, NULL, NULL, 0); } if (ti->flags) { flags_len = sizeof(ti->flags); sc_format_asn1_entry(asn1_toki_attrs + 5, &ti->flags, &flags_len, 1); } else { sc_format_asn1_entry(asn1_toki_attrs + 5, NULL, NULL, 0); } if (ti->num_seInfo) sc_format_asn1_entry(asn1_toki_attrs + 6, ti->seInfo, &ti->num_seInfo, 1); else sc_format_asn1_entry(asn1_toki_attrs + 6, NULL, NULL, 0); sc_format_asn1_entry(asn1_toki_attrs + 7, NULL, NULL, 0); if (ti->supported_algos[0].reference) sc_format_asn1_entry(asn1_toki_attrs + 8, asn1_supported_algorithms, NULL, 1); else sc_format_asn1_entry(asn1_toki_attrs + 8, NULL, NULL, 0); sc_format_asn1_entry(asn1_toki_attrs + 9, NULL, NULL, 0); sc_format_asn1_entry(asn1_toki_attrs + 10, NULL, NULL, 0); if (ti->last_update.path.len) { sc_format_asn1_entry(asn1_last_update + 0, &ti->last_update.path, NULL, 1); sc_format_asn1_entry(asn1_toki_attrs + 11, asn1_last_update, NULL, 1); } else if (ti->last_update.gtime != NULL) { last_upd_len = strlen(ti->last_update.gtime); sc_format_asn1_entry(asn1_last_update + 0, ti->last_update.gtime, &last_upd_len, 1); sc_format_asn1_entry(asn1_toki_attrs + 11, asn1_last_update, NULL, 1); } else { sc_format_asn1_entry(asn1_toki_attrs + 11, NULL, NULL, 0); } sc_format_asn1_entry(asn1_toki_attrs + 12, NULL, NULL, 0); if (sc_valid_oid(&ti->profile_indication.oid)) { sc_format_asn1_entry(asn1_profile_indication + 0, &ti->profile_indication.oid, NULL, 1); sc_format_asn1_entry(asn1_toki_attrs + 13, asn1_profile_indication, NULL, 1); } else if (ti->profile_indication.name) { pi_len = strlen(ti->profile_indication.name); sc_format_asn1_entry(asn1_profile_indication + 1, ti->profile_indication.name, &pi_len, 1); sc_format_asn1_entry(asn1_toki_attrs + 13, asn1_profile_indication, NULL, 1); } else { sc_format_asn1_entry(asn1_toki_attrs + 13, NULL, NULL, 0); } sc_format_asn1_entry(asn1_tokeninfo, asn1_toki_attrs, NULL, 1); r = sc_asn1_encode(ctx, asn1_tokeninfo, buf, buflen); LOG_TEST_RET(ctx, r, "sc_asn1_encode() failed"); return SC_SUCCESS; } static const struct sc_asn1_entry c_asn1_ddo[] = { { "oid", SC_ASN1_OBJECT, SC_ASN1_TAG_OBJECT, SC_ASN1_OPTIONAL, NULL, NULL }, { "odfPath", SC_ASN1_PATH, SC_ASN1_CONS | SC_ASN1_TAG_SEQUENCE, SC_ASN1_OPTIONAL, NULL, NULL }, { "tokenInfoPath", SC_ASN1_PATH, SC_ASN1_CONS | SC_ASN1_CTX | 0, SC_ASN1_OPTIONAL, NULL, NULL }, { "unusedPath", SC_ASN1_PATH, SC_ASN1_CONS | SC_ASN1_CTX | 1, SC_ASN1_OPTIONAL, NULL, NULL }, /* According to PKCS#15 v1.1 here is the place for the future extensions. * The following data are used when ODF record points to the xDF files in a different application. */ { "ddoIIN", SC_ASN1_OCTET_STRING, SC_ASN1_APP | 0x02, SC_ASN1_OPTIONAL, NULL, NULL }, { "ddoAID", SC_ASN1_OCTET_STRING, SC_ASN1_APP | 0x0F, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; static void fix_authentic_ddo(struct sc_pkcs15_card *p15card) { /* AuthentIC v3.2 card has invalid ODF and tokenInfo paths encoded into DDO. * Cleanup this attributes -- default values must be OK. */ if (p15card->card->type == SC_CARD_TYPE_OBERTHUR_AUTHENTIC_3_2) { sc_file_free(p15card->file_odf); p15card->file_odf = NULL; sc_file_free(p15card->file_tokeninfo); p15card->file_tokeninfo = NULL; } } static int parse_ddo(struct sc_pkcs15_card *p15card, const u8 * buf, size_t buflen) { struct sc_context *ctx = p15card->card->ctx; struct sc_asn1_entry asn1_ddo[7]; sc_path_t odf_path, ti_path, us_path; struct sc_iid iid; struct sc_aid aid; int r; LOG_FUNC_CALLED(ctx); iid.len = sizeof(iid.value); aid.len = sizeof(aid.value); sc_copy_asn1_entry(c_asn1_ddo, asn1_ddo); sc_format_asn1_entry(asn1_ddo + 1, &odf_path, NULL, 0); sc_format_asn1_entry(asn1_ddo + 2, &ti_path, NULL, 0); sc_format_asn1_entry(asn1_ddo + 3, &us_path, NULL, 0); sc_format_asn1_entry(asn1_ddo + 4, iid.value, &iid.len, 0); sc_format_asn1_entry(asn1_ddo + 5, aid.value, &aid.len, 0); r = sc_asn1_decode(ctx, asn1_ddo, buf, buflen, NULL, NULL); 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; p15card->file_unusedspace->path = us_path; } if (asn1_ddo[4].flags & SC_ASN1_PRESENT) { sc_debug(ctx, SC_LOG_DEBUG_ASN1, "DDO.IID '%s'", sc_dump_hex(iid.value, iid.len)); memcpy(&p15card->app->ddo.iid, &iid, sizeof(struct sc_iid)); } if (asn1_ddo[5].flags & SC_ASN1_PRESENT) { sc_debug(ctx, SC_LOG_DEBUG_ASN1, "DDO.AID '%s'", sc_dump_hex(aid.value, aid.len)); memcpy(&p15card->app->ddo.aid, &aid, sizeof(struct sc_aid)); } fix_authentic_ddo(p15card); LOG_FUNC_RETURN(ctx, SC_SUCCESS); mem_err: sc_file_free(p15card->file_odf); p15card->file_odf = NULL; sc_file_free(p15card->file_tokeninfo); p15card->file_tokeninfo = NULL; sc_file_free(p15card->file_unusedspace); p15card->file_unusedspace = NULL; LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); } char * 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] = {0}; size_t lupdate_len = sizeof(last_update) - 1; int r, content_len; size_t size; if (p15card->tokeninfo->last_update.gtime) goto done; if (!p15card->tokeninfo->last_update.path.len) return NULL; r = sc_select_file(p15card->card, &p15card->tokeninfo->last_update.path, &file); if (r < 0) return NULL; size = file->size ? file->size : 1024; sc_file_free(file); content = calloc(1, size); if (!content) return NULL; r = sc_read_binary(p15card->card, 0, content, size, 0); if (r < 0) { free(content); return NULL; } content_len = r; sc_copy_asn1_entry(c_asn1_last_update, asn1_last_update); sc_format_asn1_entry(asn1_last_update + 0, last_update, &lupdate_len, 0); r = sc_asn1_decode(ctx, asn1_last_update, content, content_len, NULL, NULL); free(content); if (r < 0) 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; } static const struct sc_asn1_entry c_asn1_odf[] = { { "privateKeys", SC_ASN1_STRUCT, SC_ASN1_CTX | 0 | SC_ASN1_CONS, 0, NULL, NULL }, { "publicKeys", SC_ASN1_STRUCT, SC_ASN1_CTX | 1 | SC_ASN1_CONS, 0, NULL, NULL }, { "trustedPublicKeys", SC_ASN1_STRUCT, SC_ASN1_CTX | 2 | SC_ASN1_CONS, 0, NULL, NULL }, { "secretKeys", SC_ASN1_STRUCT, SC_ASN1_CTX | 3 | SC_ASN1_CONS, 0, NULL, NULL }, { "certificates", SC_ASN1_STRUCT, SC_ASN1_CTX | 4 | SC_ASN1_CONS, 0, NULL, NULL }, { "trustedCertificates", SC_ASN1_STRUCT, SC_ASN1_CTX | 5 | SC_ASN1_CONS, 0, NULL, NULL }, { "usefulCertificates", SC_ASN1_STRUCT, SC_ASN1_CTX | 6 | SC_ASN1_CONS, 0, NULL, NULL }, { "dataObjects", SC_ASN1_STRUCT, SC_ASN1_CTX | 7 | SC_ASN1_CONS, 0, NULL, NULL }, { "authObjects", SC_ASN1_STRUCT, SC_ASN1_CTX | 8 | SC_ASN1_CONS, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; static const unsigned int odf_indexes[] = { SC_PKCS15_PRKDF, SC_PKCS15_PUKDF, SC_PKCS15_PUKDF_TRUSTED, SC_PKCS15_SKDF, SC_PKCS15_CDF, SC_PKCS15_CDF_TRUSTED, SC_PKCS15_CDF_USEFUL, SC_PKCS15_DODF, SC_PKCS15_AODF, }; static int parse_odf(const unsigned char * buf, size_t buflen, struct sc_pkcs15_card *p15card) { const unsigned char *p = buf; size_t left = buflen; int r, i, type; struct sc_path path; struct sc_asn1_entry asn1_obj_or_path[] = { { "path", SC_ASN1_PATH, SC_ASN1_CONS | SC_ASN1_SEQUENCE, 0, &path, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; struct sc_asn1_entry asn1_odf[10]; sc_copy_asn1_entry(c_asn1_odf, asn1_odf); for (i = 0; asn1_odf[i].name != NULL; i++) sc_format_asn1_entry(asn1_odf + i, asn1_obj_or_path, NULL, 0); while (left > 0) { r = sc_asn1_decode_choice(p15card->card->ctx, asn1_odf, p, left, &p, &left); if (r == SC_ERROR_ASN1_END_OF_CONTENTS) break; if (r < 0) return r; type = 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; } int sc_pkcs15_encode_odf(struct sc_context *ctx, struct sc_pkcs15_card *p15card, unsigned char **buf, size_t *buflen) { struct sc_path path; struct sc_asn1_entry asn1_obj_or_path[] = { { "path", SC_ASN1_PATH, SC_ASN1_CONS | SC_ASN1_SEQUENCE, 0, &path, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; struct sc_asn1_entry *asn1_paths = NULL; struct sc_asn1_entry *asn1_odf = NULL; int df_count = 0, r, c = 0; const int nr_indexes = sizeof(odf_indexes)/sizeof(odf_indexes[0]); struct sc_pkcs15_df *df; df = p15card->df_list; while (df != NULL) { df_count++; df = df->next; }; if (df_count == 0) LOG_TEST_RET(ctx, SC_ERROR_OBJECT_NOT_FOUND, "No DF's found."); asn1_odf = malloc(sizeof(struct sc_asn1_entry) * (df_count + 1)); if (asn1_odf == NULL) { r = SC_ERROR_OUT_OF_MEMORY; goto err; } asn1_paths = malloc(sizeof(struct sc_asn1_entry) * (df_count * 2)); if (asn1_paths == NULL) { r = SC_ERROR_OUT_OF_MEMORY; goto err; } for (df = p15card->df_list; df != NULL; df = df->next) { int j, type = -1; for (j = 0; j < nr_indexes; j++) if (odf_indexes[j] == df->type) { type = j; break; } if (type == -1) { sc_log(ctx, "Unsupported DF type."); continue; } asn1_odf[c] = c_asn1_odf[type]; sc_format_asn1_entry(asn1_odf + c, asn1_paths + 2*c, NULL, 1); sc_copy_asn1_entry(asn1_obj_or_path, asn1_paths + 2*c); sc_format_asn1_entry(asn1_paths + 2*c, &df->path, NULL, 1); c++; } asn1_odf[c].name = NULL; r = sc_asn1_encode(ctx, asn1_odf, buf, buflen); err: if (asn1_paths != NULL) free(asn1_paths); if (asn1_odf != NULL) free(asn1_odf); return r; } struct sc_pkcs15_card * sc_pkcs15_card_new(void) { struct sc_pkcs15_card *p15card; p15card = calloc(1, sizeof(struct sc_pkcs15_card)); if (p15card == NULL) return NULL; p15card->tokeninfo = calloc(1, sizeof(struct sc_pkcs15_tokeninfo)); if (p15card->tokeninfo == NULL) { free(p15card); return NULL; } p15card->magic = SC_PKCS15_CARD_MAGIC; return p15card; } struct sc_pkcs15_tokeninfo * sc_pkcs15_tokeninfo_new(void) { struct sc_pkcs15_tokeninfo *tokeninfo; tokeninfo = calloc(1, sizeof(struct sc_pkcs15_tokeninfo)); if (tokeninfo == NULL) { return 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_tokeninfo(struct sc_pkcs15_tokeninfo *tokeninfo) { if (!tokeninfo) return; 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); free(p15card->app->ddo.value); free(p15card->app); p15card->app = NULL; } } void sc_pkcs15_card_free(struct sc_pkcs15_card *p15card) { if (p15card == NULL || p15card->magic != SC_PKCS15_CARD_MAGIC) return; if (p15card->ops.clear) p15card->ops.clear(p15card); /* For more complicated MD data a dedicated release procedure * has to be implemented. */ 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); p15card->unusedspace_read = 0; sc_file_free(p15card->file_app); sc_file_free(p15card->file_tokeninfo); sc_file_free(p15card->file_odf); sc_file_free(p15card->file_unusedspace); p15card->magic = 0; sc_pkcs15_free_tokeninfo(p15card->tokeninfo); sc_pkcs15_free_app(p15card); free(p15card); } void sc_pkcs15_card_clear(struct sc_pkcs15_card *p15card) { if (p15card == NULL) return; if (p15card->ops.clear) p15card->ops.clear(p15card); p15card->flags = 0; p15card->tokeninfo->version = 0; p15card->tokeninfo->flags = 0; sc_pkcs15_remove_objects(p15card); sc_pkcs15_remove_dfs(p15card); p15card->df_list = NULL; sc_file_free(p15card->file_app); p15card->file_app = NULL; sc_file_free(p15card->file_tokeninfo); p15card->file_tokeninfo = NULL; sc_file_free(p15card->file_odf); p15card->file_odf = NULL; sc_file_free(p15card->file_unusedspace); p15card->file_unusedspace = 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++) free(p15card->tokeninfo->seInfo[i]); free(p15card->tokeninfo->seInfo); p15card->tokeninfo->seInfo = NULL; p15card->tokeninfo->num_seInfo = 0; } sc_pkcs15_free_app(p15card); } struct sc_app_info * sc_find_app(struct sc_card *card, struct sc_aid *aid) { int ii; if (card->app_count <= 0) return NULL; if (!aid || !aid->len) return card->app[0]; for (ii=0; ii < card->app_count; ii++) { if (card->app[ii]->aid.len != aid->len) continue; if (memcmp(card->app[ii]->aid.value, aid->value, aid->len)) continue; return card->app[ii]; } return NULL; } static struct sc_app_info * sc_dup_app_info(const struct sc_app_info *info) { struct sc_app_info *out = calloc(1, sizeof(struct sc_app_info)); if (!out) return NULL; memcpy(out, info, sizeof(struct sc_app_info)); if (info->label) { out->label = strdup(info->label); if (!out->label) { free(out); return NULL; } } else out->label = NULL; out->ddo.value = malloc(info->ddo.len); if (!out->ddo.value) { free(out->label); free(out); return NULL; } memcpy(out->ddo.value, info->ddo.value, info->ddo.len); out->ddo.len = info->ddo.len; return out; } struct sc_app_info * sc_pkcs15_get_application_by_type(struct sc_card * card, char *app_type) { struct sc_app_info *out = NULL; scconf_block *conf_block = NULL; int i, rv; if (!card) return NULL; if (card->app_count < 0) { rv = sc_enum_apps(card); if (rv < 0 && rv != SC_ERROR_FILE_NOT_FOUND) return NULL; } conf_block = sc_get_conf_block(card->ctx, "framework", "pkcs15", 1); if (!conf_block) return NULL; for (i = 0; i < card->app_count; i++) { struct sc_app_info *app_info = card->app[i]; scconf_block **blocks = NULL; char str_path[SC_MAX_AID_STRING_SIZE]; sc_bin_to_hex(app_info->aid.value, app_info->aid.len, str_path, sizeof(str_path), 0); blocks = scconf_find_blocks(card->ctx->conf, conf_block, "application", str_path); if (blocks) { if (blocks[0]) { char *type = (char *)scconf_get_str(blocks[0], "type", app_type); if (!strcmp(type, app_type)) { out = app_info; free(blocks); break; } } free(blocks); } } return out; } int sc_pkcs15_bind_internal(struct sc_pkcs15_card *p15card, struct sc_aid *aid) { struct sc_path tmppath; struct sc_card *card = p15card->card; struct sc_context *ctx = card->ctx; struct sc_pkcs15_tokeninfo tokeninfo; struct sc_pkcs15_df *df; const struct sc_app_info *info = NULL; unsigned char *buf = NULL; size_t len; int err, ok = 0; LOG_FUNC_CALLED(ctx); /* Enumerate apps now */ if (card->app_count < 0) { err = sc_enum_apps(card); 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; goto end; } sc_format_path("3F005015", &p15card->file_app->path); 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; goto end; } if (info->path.len) p15card->file_app->path = info->path; if (info->ddo.value && info->ddo.len) parse_ddo(p15card, info->ddo.value, info->ddo.len); } else if (aid) { sc_log(ctx, "Application '%s' not found", sc_dump_hex(aid->value, aid->len)); err = SC_ERROR_INVALID_ARGUMENTS; goto end; } sc_log(ctx, "application path '%s'", sc_print_path(&p15card->file_app->path)); /* Check if pkcs15 directory exists */ err = sc_select_file(card, &p15card->file_app->path, NULL); /* If the above test failed on cards without EF(DIR), * try to continue read ODF from 3F005031. -aet */ if ((err != SC_SUCCESS) && (card->app_count < 1)) { sc_format_path("3F00", &p15card->file_app->path); err = SC_SUCCESS; } if (err < 0) { sc_log (ctx, "Cannot select application path"); goto end; } if (p15card->file_odf == NULL) { /* check if an ODF is present; we don't know yet whether we have a pkcs15 card */ sc_format_path("5031", &tmppath); err = sc_pkcs15_make_absolute_path(&p15card->file_app->path, &tmppath); if (err != SC_SUCCESS) { sc_log(ctx, "Cannot make absolute path to EF(ODF); error:%i", err); goto end; } sc_log(ctx, "absolute path to EF(ODF) %s", sc_print_path(&tmppath)); err = sc_select_file(card, &tmppath, &p15card->file_odf); } else { tmppath = p15card->file_odf->path; sc_file_free(p15card->file_odf); p15card->file_odf = NULL; err = sc_select_file(card, &tmppath, &p15card->file_odf); } if (err != SC_SUCCESS) { sc_log(ctx, "EF(ODF) not found in '%s'", sc_print_path(&tmppath)); goto end; } len = p15card->file_odf->size; if (!len) { 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) { 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 = (int)len; } if (err < 0) { err = sc_read_binary(card, 0, buf, len, 0); 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); } } if (parse_odf(buf, len, p15card)) { err = SC_ERROR_PKCS15_APP_NOT_FOUND; sc_log(ctx, "Unable to parse ODF"); goto end; } free(buf); buf = NULL; sc_log(ctx, "The following DFs were found:"); for (df = p15card->df_list; df; df = df->next) sc_log(ctx, " DF type %u, path %s, index %u, count %d", df->type, sc_print_path(&df->path), df->path.index, df->path.count); if (p15card->file_tokeninfo == NULL) { sc_format_path("5032", &tmppath); err = sc_pkcs15_make_absolute_path(&p15card->file_app->path, &tmppath); if (err != SC_SUCCESS) { sc_log(ctx, "Cannot make absolute path to EF(TokenInfo); error:%i", err); goto end; } sc_log(ctx, "absolute path to EF(TokenInfo) %s", sc_print_path(&tmppath)); } else { tmppath = p15card->file_tokeninfo->path; sc_file_free(p15card->file_tokeninfo); p15card->file_tokeninfo = NULL; } err = sc_select_file(card, &tmppath, &p15card->file_tokeninfo); if (err) { sc_log(ctx, "cannot select EF(TokenInfo) file: %s", sc_strerror(err)); goto end; } len = p15card->file_tokeninfo->size; if (!len) { 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) { 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 = (int)len; } if (err < 0) { err = sc_read_binary(card, 0, buf, len, 0); if (err <= 2) { 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); } } memset(&tokeninfo, 0, sizeof(tokeninfo)); err = sc_pkcs15_parse_tokeninfo(ctx, &tokeninfo, buf, (size_t)err); if (err != SC_SUCCESS) { sc_log(ctx, "cannot parse TokenInfo content: %s", sc_strerror(err)); 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; if (!serial) { err = SC_ERROR_OUT_OF_MEMORY; goto end; } for(ii=0;iiserialnr.len;ii++) sprintf(serial + ii*2, "%02X", *(card->serialnr.value + ii)); p15card->tokeninfo->serial_number = serial; sc_log(ctx, "p15card->tokeninfo->serial_number %s", p15card->tokeninfo->serial_number); } ok = 1; end: if(buf != NULL) free(buf); if (!ok) { sc_pkcs15_card_clear(p15card); if (err == SC_ERROR_FILE_NOT_FOUND) err = SC_ERROR_WRONG_CARD; LOG_FUNC_RETURN(ctx, err); } LOG_FUNC_RETURN(ctx, SC_SUCCESS); } 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", "dnie", "edo", "esteid2018", "flex", "cyberflex", "gemsafeV1", "idprime", "itacns", "jpki", "MaskTech", "mcrd", "myeid", "npa", "nqapplet", "tcos", }; 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; 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"); 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 = 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; if (0 == strcmp(ctx->app_name, "tokend")) { pin_protected_certificate = "ignore"; } else { pin_protected_certificate = "protect"; } private_certificate = ""; conf_block = sc_get_conf_block(ctx, "framework", "pkcs15", 1); if (conf_block) { 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); 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) { sc_log(ctx, "sc_lock() failed: %s", sc_strerror(r)); sc_pkcs15_card_free(p15card); LOG_FUNC_RETURN(ctx, r); } enable_emu = scconf_get_bool(conf_block, "enable_pkcs15_emulation", 1); if (enable_emu) { sc_log(ctx, "PKCS#15 emulation enabled"); emu_first = scconf_get_bool(conf_block, "try_emulation_first", 0); if (emu_first || sc_pkcs15_is_emulation_only(card)) { r = sc_pkcs15_bind_synthetic(p15card, aid); if (r == SC_SUCCESS) goto done; r = sc_pkcs15_bind_internal(p15card, aid); if (r < 0) goto error; } else { r = sc_pkcs15_bind_internal(p15card, aid); if (r == SC_SUCCESS) goto done; r = sc_pkcs15_bind_synthetic(p15card, aid); if (r < 0) goto error; } } else { r = sc_pkcs15_bind_internal(p15card, aid); if (r < 0) goto error; } done: *p15card_out = p15card; sc_unlock(card); LOG_FUNC_RETURN(ctx, SC_SUCCESS); error: sc_unlock(card); sc_pkcs15_card_free(p15card); LOG_FUNC_RETURN(ctx, r); } int sc_pkcs15_unbind(struct sc_pkcs15_card *p15card) { if (p15card == NULL || p15card->magic != SC_PKCS15_CARD_MAGIC) { return SC_ERROR_INVALID_ARGUMENTS; } LOG_FUNC_CALLED(p15card->card->ctx); if (p15card->dll_handle) sc_dlclose(p15card->dll_handle); sc_pkcs15_pincache_clear(p15card); sc_pkcs15_card_free(p15card); return 0; } static int __sc_pkcs15_search_objects(struct sc_pkcs15_card *p15card, unsigned int class_mask, unsigned int type, int (*func)(sc_pkcs15_object_t *, void *), void *func_arg, sc_pkcs15_object_t **ret, size_t ret_size) { struct sc_pkcs15_object *obj = NULL; struct sc_pkcs15_df *df = NULL; unsigned int df_mask = 0; size_t match_count = 0; int r; if (type) class_mask |= SC_PKCS15_TYPE_TO_CLASS(type); /* 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))) { LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_INVALID_ARGUMENTS); } if (class_mask & SC_PKCS15_SEARCH_CLASS_PRKEY) df_mask |= (1 << SC_PKCS15_PRKDF); if (class_mask & SC_PKCS15_SEARCH_CLASS_PUBKEY) df_mask |= (1 << SC_PKCS15_PUKDF) | (1 << SC_PKCS15_PUKDF_TRUSTED); if (class_mask & SC_PKCS15_SEARCH_CLASS_CERT) df_mask |= (1 << SC_PKCS15_CDF) | (1 << SC_PKCS15_CDF_TRUSTED) | (1 << SC_PKCS15_CDF_USEFUL); if (class_mask & SC_PKCS15_SEARCH_CLASS_DATA) df_mask |= (1 << SC_PKCS15_DODF); if (class_mask & SC_PKCS15_SEARCH_CLASS_AUTH) df_mask |= (1 << SC_PKCS15_AODF); if (class_mask & SC_PKCS15_SEARCH_CLASS_SKEY) df_mask |= (1 << SC_PKCS15_SKDF); /* Make sure all the DFs we want to search have been * enumerated. */ for (df = p15card->df_list; df != NULL; df = df->next) { if (!(df_mask & (1 << df->type))) { continue; } if (df->enumerated) continue; /* Enumerate the DF's, so p15card->obj_list is populated. */ if (p15card->ops.parse_df) r = p15card->ops.parse_df(p15card, df); else r = sc_pkcs15_parse_df(p15card, df); if (r != SC_SUCCESS) continue; } /* And now loop over all objects */ for (obj = p15card->obj_list; obj != NULL; obj = obj->next) { /* Check object type */ if (!(class_mask & SC_PKCS15_TYPE_TO_CLASS(obj->type))) continue; if (type != 0 && obj->type != type && (obj->type & SC_PKCS15_TYPE_CLASS_MASK) != type) continue; /* Potential candidate, apply search function */ if (func != NULL && func(obj, func_arg) <= 0) continue; /* Okay, we have a match. */ match_count++; if (!ret || ret_size <= 0) continue; ret[match_count-1] = obj; if (ret_size <= match_count) break; } return (int)match_count; } int sc_pkcs15_get_objects(struct sc_pkcs15_card *p15card, unsigned int type, struct sc_pkcs15_object **ret, size_t ret_size) { return sc_pkcs15_get_objects_cond(p15card, type, NULL, NULL, ret, ret_size); } static int compare_obj_id(struct sc_pkcs15_object *obj, const struct sc_pkcs15_id *id) { void *data = obj->data; switch (obj->type & SC_PKCS15_TYPE_CLASS_MASK) { case SC_PKCS15_TYPE_CERT: return sc_pkcs15_compare_id(&((struct sc_pkcs15_cert_info *) data)->id, id); case SC_PKCS15_TYPE_PRKEY: return sc_pkcs15_compare_id(&((struct sc_pkcs15_prkey_info *) data)->id, id); case SC_PKCS15_TYPE_PUBKEY: return sc_pkcs15_compare_id(&((struct sc_pkcs15_pubkey_info *) data)->id, id); case SC_PKCS15_TYPE_SKEY: return sc_pkcs15_compare_id(&((struct sc_pkcs15_skey_info *) data)->id, id); case SC_PKCS15_TYPE_AUTH: return sc_pkcs15_compare_id(&((struct sc_pkcs15_auth_info *) data)->auth_id, id); case SC_PKCS15_TYPE_DATA_OBJECT: return sc_pkcs15_compare_id(&((struct sc_pkcs15_data_info *) data)->id, id); } return 0; } static int sc_obj_app_oid(struct sc_pkcs15_object *obj, const struct sc_object_id *app_oid) { if ((obj->type & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_DATA_OBJECT) return sc_compare_oid(&((struct sc_pkcs15_data_info *) obj->data)->app_oid, app_oid); return 0; } static int compare_obj_usage(struct sc_pkcs15_object *obj, unsigned int mask, unsigned int value) { void *data = obj->data; unsigned int usage; switch (obj->type & SC_PKCS15_TYPE_CLASS_MASK) { case SC_PKCS15_TYPE_PRKEY: usage = ((struct sc_pkcs15_prkey_info *) data)->usage; break; case SC_PKCS15_TYPE_PUBKEY: usage = ((struct sc_pkcs15_pubkey_info *) data)->usage; break; default: return 0; } return (usage & mask & value) != 0; } static int compare_obj_flags(struct sc_pkcs15_object *obj, unsigned int mask, unsigned int value) { struct sc_pkcs15_auth_info *auth_info; unsigned int flags; switch (obj->type & SC_PKCS15_TYPE_CLASS_MASK) { case SC_PKCS15_TYPE_AUTH: auth_info = (struct sc_pkcs15_auth_info *) obj->data; if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return 0; flags = auth_info->attrs.pin.flags; break; default: return 0; } return !((flags ^ value) & mask); } static int compare_obj_reference(struct sc_pkcs15_object *obj, int value) { struct sc_pkcs15_auth_info *auth_info; void *data = obj->data; int reference; switch (obj->type & SC_PKCS15_TYPE_CLASS_MASK) { case SC_PKCS15_TYPE_AUTH: auth_info = (struct sc_pkcs15_auth_info *) obj->data; if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return 0; reference = auth_info->attrs.pin.reference; break; case SC_PKCS15_TYPE_PRKEY: reference = ((struct sc_pkcs15_prkey_info *) data)->key_reference; break; default: return 0; } return reference == value; } static int compare_obj_path(struct sc_pkcs15_object *obj, const struct sc_path *path) { void *data = obj->data; switch (obj->type & SC_PKCS15_TYPE_CLASS_MASK) { case SC_PKCS15_TYPE_PRKEY: return sc_compare_path(&((struct sc_pkcs15_prkey_info *) data)->path, path); case SC_PKCS15_TYPE_PUBKEY: return sc_compare_path(&((struct sc_pkcs15_pubkey_info *) data)->path, path); case SC_PKCS15_TYPE_SKEY: return sc_compare_path(&((struct sc_pkcs15_skey_info *) data)->path, path); case SC_PKCS15_TYPE_CERT: return sc_compare_path(&((struct sc_pkcs15_cert_info *) data)->path, path); case SC_PKCS15_TYPE_AUTH: return sc_compare_path(&((struct sc_pkcs15_auth_info *) data)->path, path); case SC_PKCS15_TYPE_DATA_OBJECT: return sc_compare_path(&((struct sc_pkcs15_data_info *) data)->path, path); } return 0; } static int compare_obj_data_name(struct sc_pkcs15_object *obj, const char *app_label, const char *label) { struct sc_pkcs15_data_info *cinfo = (struct sc_pkcs15_data_info *) obj->data; if (obj->type != SC_PKCS15_TYPE_DATA_OBJECT) return 0; return !strncmp(cinfo->app_label, app_label, sizeof cinfo->app_label) && !strncmp(obj->label, label, sizeof obj->label); } static int compare_obj_key(struct sc_pkcs15_object *obj, void *arg) { struct sc_pkcs15_search_key *sk = (struct sc_pkcs15_search_key *) arg; if (sk->id && !compare_obj_id(obj, sk->id)) return 0; if (sk->app_oid && !sc_obj_app_oid(obj, sk->app_oid)) return 0; if (sk->usage_mask && !compare_obj_usage(obj, sk->usage_mask, sk->usage_value)) return 0; if (sk->flags_mask && !compare_obj_flags(obj, sk->flags_mask, sk->flags_value)) return 0; if (sk->match_reference && !compare_obj_reference(obj, sk->reference)) return 0; if (sk->path && !compare_obj_path(obj, sk->path)) return 0; if ( sk->app_label && sk->label && !compare_obj_data_name(obj, sk->app_label, sk->label) ) { return 0; } return 1; } static int find_by_key(struct sc_pkcs15_card *p15card, unsigned int type, struct sc_pkcs15_search_key *sk, struct sc_pkcs15_object **out) { int r; r = sc_pkcs15_get_objects_cond(p15card, type, compare_obj_key, sk, out, 1); if (r < 0) return r; if (r == 0) return SC_ERROR_OBJECT_NOT_FOUND; return 0; } int sc_pkcs15_search_objects(struct sc_pkcs15_card *p15card, struct sc_pkcs15_search_key *sk, struct sc_pkcs15_object **ret, size_t ret_size) { return __sc_pkcs15_search_objects(p15card, sk->class_mask, sk->type, compare_obj_key, sk, ret, ret_size); } int sc_pkcs15_get_objects_cond(struct sc_pkcs15_card *p15card, unsigned int type, int (* func)(struct sc_pkcs15_object *, void *), void *func_arg, struct sc_pkcs15_object **ret, size_t ret_size) { return __sc_pkcs15_search_objects(p15card, 0, type, func, func_arg, ret, ret_size); } int sc_pkcs15_find_object_by_id(struct sc_pkcs15_card *p15card, unsigned int type, const struct sc_pkcs15_id *id, struct sc_pkcs15_object **out) { struct sc_pkcs15_search_key sk; int r; memset(&sk, 0, sizeof(sk)); sk.id = id; r = __sc_pkcs15_search_objects(p15card, 0, type, compare_obj_key, &sk, out, 1); if (r < 0) return r; if (r == 0) return SC_ERROR_OBJECT_NOT_FOUND; return 0; } int sc_pkcs15_find_cert_by_id(struct sc_pkcs15_card *p15card, const struct sc_pkcs15_id *id, struct sc_pkcs15_object **out) { return sc_pkcs15_find_object_by_id(p15card, SC_PKCS15_TYPE_CERT, id, out); } int sc_pkcs15_find_prkey_by_id(struct sc_pkcs15_card *p15card, const struct sc_pkcs15_id *id, struct sc_pkcs15_object **out) { return sc_pkcs15_find_object_by_id(p15card, SC_PKCS15_TYPE_PRKEY, id, out); } int sc_pkcs15_find_pubkey_by_id(struct sc_pkcs15_card *p15card, const struct sc_pkcs15_id *id, struct sc_pkcs15_object **out) { return sc_pkcs15_find_object_by_id(p15card, SC_PKCS15_TYPE_PUBKEY, id, out); } int sc_pkcs15_find_skey_by_id(struct sc_pkcs15_card *p15card, const struct sc_pkcs15_id *id, struct sc_pkcs15_object **out) { return sc_pkcs15_find_object_by_id(p15card, SC_PKCS15_TYPE_SKEY, id, out); } int sc_pkcs15_find_pin_by_auth_id(struct sc_pkcs15_card *p15card, const struct sc_pkcs15_id *id, struct sc_pkcs15_object **out) { return sc_pkcs15_find_object_by_id(p15card, SC_PKCS15_TYPE_AUTH, id, out); } int sc_pkcs15_find_pin_by_reference(struct sc_pkcs15_card *p15card, const sc_path_t *path, int reference, struct sc_pkcs15_object **out) { struct sc_pkcs15_search_key sk; memset(&sk, 0, sizeof(sk)); sk.match_reference = 1; sk.reference = reference; sk.path = path; return find_by_key(p15card, SC_PKCS15_TYPE_AUTH_PIN, &sk, out); } int sc_pkcs15_find_pin_by_type_and_reference(struct sc_pkcs15_card *p15card, const struct sc_path *path, unsigned auth_method, int reference, struct sc_pkcs15_object **out) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_object *auth_objs[0x10]; size_t nn_objs, ii; int r; /* Get all existing pkcs15 AUTH objects */ r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_AUTH_PIN, auth_objs, 0x10); LOG_TEST_RET(ctx, r, "Get PKCS#15 AUTH objects error"); nn_objs = r; for (ii=0; iidata; if (auth_info->auth_method != auth_method) continue; if (auth_info->auth_type == SC_PKCS15_PIN_AUTH_TYPE_PIN) if (auth_info->attrs.pin.reference != reference) continue; if (path && !sc_compare_path(&auth_info->path, path)) continue; if (out) *out = auth_objs[ii]; return SC_SUCCESS; } return SC_ERROR_OBJECT_NOT_FOUND; } int sc_pkcs15_find_so_pin(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object **out) { struct sc_pkcs15_search_key sk; memset(&sk, 0, sizeof(sk)); sk.flags_mask = sk.flags_value = SC_PKCS15_PIN_FLAG_SO_PIN; return find_by_key(p15card, SC_PKCS15_TYPE_AUTH_PIN, &sk, out); } int sc_pkcs15_find_pin_by_flags(struct sc_pkcs15_card *p15card, unsigned flags, unsigned mask, int *index, struct sc_pkcs15_object **out) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_object *auths[SC_PKCS15_MAX_PINS]; int r, i, num, idx = 0; LOG_FUNC_CALLED(ctx); sc_log(ctx, "Find PIN flags:0x%X, mask:0x%X, index:%i", flags, mask, index ? *index : -1); if (index) idx = *index; /* Get authentication PKCS#15 objects that are present in the given application */ r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_AUTH_PIN, auths, SC_PKCS15_MAX_PINS); if (r < 0) return r; num = r; for (i=idx; idata; if (!pin_info || pin_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) continue; if ((pin_info->attrs.pin.flags & mask) != flags) continue; if (out) *out = *(auths + i); if (index) *index = i; LOG_FUNC_RETURN(ctx, SC_SUCCESS); } LOG_FUNC_RETURN(ctx, SC_ERROR_OBJECT_NOT_FOUND); } int sc_pkcs15_find_data_object_by_id(struct sc_pkcs15_card *p15card, const struct sc_pkcs15_id *id, struct sc_pkcs15_object **out) { return sc_pkcs15_find_object_by_id(p15card, SC_PKCS15_TYPE_DATA_OBJECT, id, out); } int sc_pkcs15_find_data_object_by_app_oid(struct sc_pkcs15_card *p15card, const struct sc_object_id *app_oid, struct sc_pkcs15_object **out) { struct sc_pkcs15_search_key sk; int r; memset(&sk, 0, sizeof(sk)); sk.app_oid = app_oid; r = __sc_pkcs15_search_objects(p15card, 0, SC_PKCS15_TYPE_DATA_OBJECT, compare_obj_key, &sk, out, 1); if (r < 0) return r; if (r == 0) return SC_ERROR_OBJECT_NOT_FOUND; return 0; } int sc_pkcs15_find_data_object_by_name(struct sc_pkcs15_card *p15card, const char *app_label, const char *label, struct sc_pkcs15_object **out) { struct sc_pkcs15_search_key sk; int r; memset(&sk, 0, sizeof(sk)); sk.app_label = app_label; sk.label = label; r = __sc_pkcs15_search_objects(p15card, 0, SC_PKCS15_TYPE_DATA_OBJECT, compare_obj_key, &sk, out, 1); if (r < 0) return r; if (r == 0) return SC_ERROR_OBJECT_NOT_FOUND; return 0; } int sc_pkcs15_find_prkey_by_id_usage(struct sc_pkcs15_card *p15card, const struct sc_pkcs15_id *id, unsigned int usage, struct sc_pkcs15_object **out) { struct sc_pkcs15_search_key sk; memset(&sk, 0, sizeof(sk)); sk.usage_mask = sk.usage_value = usage; sk.id = id; return find_by_key(p15card, SC_PKCS15_TYPE_PRKEY, &sk, out); } int sc_pkcs15_find_prkey_by_reference(struct sc_pkcs15_card *p15card, const struct sc_path *path, int reference, struct sc_pkcs15_object **out) { struct sc_pkcs15_search_key sk; memset(&sk, 0, sizeof(sk)); sk.match_reference = 1; sk.reference = reference; sk.path = path; return find_by_key(p15card, SC_PKCS15_TYPE_PRKEY, &sk, out); } int sc_pkcs15_add_object(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *obj) { struct sc_pkcs15_object *p = p15card->obj_list; if (!obj) return 0; obj->next = obj->prev = NULL; if (p15card->obj_list == NULL) { p15card->obj_list = obj; return 0; } while (p->next != NULL) p = p->next; p->next = obj; obj->prev = p; return 0; } void sc_pkcs15_remove_object(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *obj) { if (!obj) return; else if (obj->prev == NULL) p15card->obj_list = obj->next; else obj->prev->next = obj->next; if (obj->next != NULL) obj->next->prev = obj->prev; } static void sc_pkcs15_remove_objects(struct sc_pkcs15_card *p15card) { struct sc_pkcs15_object *cur = NULL, *next = NULL; if (!p15card || !p15card->obj_list) return; for (cur = p15card->obj_list; cur; cur = next) { next = cur->next; sc_pkcs15_free_object(cur); } p15card->obj_list = NULL; } void sc_pkcs15_free_object(struct sc_pkcs15_object *obj) { if (!obj) return; switch (obj->type & SC_PKCS15_TYPE_CLASS_MASK) { case SC_PKCS15_TYPE_PRKEY: 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; case SC_PKCS15_TYPE_AUTH: sc_pkcs15_free_auth_info((sc_pkcs15_auth_info_t *)obj->data); break; default: free(obj->data); } sc_pkcs15_free_object_content(obj); free(obj); } int sc_pkcs15_add_df(struct sc_pkcs15_card *p15card, unsigned int type, const sc_path_t *path) { struct sc_pkcs15_df *p, *newdf; newdf = calloc(1, sizeof(struct sc_pkcs15_df)); if (newdf == NULL) return SC_ERROR_OUT_OF_MEMORY; newdf->path = *path; newdf->type = type; if (p15card->df_list == NULL) { p15card->df_list = newdf; return 0; } p = p15card->df_list; while (p->next != NULL) p = p->next; p->next = newdf; newdf->prev = p; return 0; } static void sc_pkcs15_remove_dfs(struct sc_pkcs15_card *p15card) { struct sc_pkcs15_df *cur = NULL, *next = NULL; if (!p15card || !p15card->df_list) return; for (cur = p15card->df_list; cur; cur = next) { next = cur->next; free(cur); } p15card->df_list = NULL; } int sc_pkcs15_encode_df(struct sc_context *ctx, struct sc_pkcs15_card *p15card, struct sc_pkcs15_df *df, unsigned char **buf_out, size_t *bufsize_out) { unsigned char *buf = NULL, *tmp = NULL, *p; size_t bufsize = 0, tmpsize; const struct sc_pkcs15_object *obj; int (* func)(struct sc_context *, const struct sc_pkcs15_object *nobj, unsigned char **nbuf, size_t *nbufsize) = NULL; int r; if (p15card == NULL || p15card->magic != SC_PKCS15_CARD_MAGIC) { return SC_ERROR_INVALID_ARGUMENTS; } switch (df->type) { case SC_PKCS15_PRKDF: func = sc_pkcs15_encode_prkdf_entry; break; case SC_PKCS15_PUKDF: case SC_PKCS15_PUKDF_TRUSTED: func = sc_pkcs15_encode_pukdf_entry; break; case SC_PKCS15_SKDF: func = sc_pkcs15_encode_skdf_entry; break; case SC_PKCS15_CDF: case SC_PKCS15_CDF_TRUSTED: case SC_PKCS15_CDF_USEFUL: func = sc_pkcs15_encode_cdf_entry; break; case SC_PKCS15_DODF: func = sc_pkcs15_encode_dodf_entry; break; case SC_PKCS15_AODF: func = sc_pkcs15_encode_aodf_entry; break; } if (func == NULL) { sc_log(ctx, "unknown DF type: %d", df->type); *buf_out = NULL; *bufsize_out = 0; return 0; } for (obj = p15card->obj_list; obj != NULL; obj = obj->next) { if (obj->df != df) continue; r = func(ctx, obj, &tmp, &tmpsize); if (r) { free(tmp); free(buf); return r; } if (!tmpsize) continue; p = (u8 *) realloc(buf, bufsize + tmpsize); if (!p) { free(tmp); free(buf); return SC_ERROR_OUT_OF_MEMORY; } buf = p; memcpy(buf + bufsize, tmp, tmpsize); free(tmp); tmp = NULL; bufsize += tmpsize; } *buf_out = buf; *bufsize_out = bufsize; return 0; } int sc_pkcs15_parse_df(struct sc_pkcs15_card *p15card, struct sc_pkcs15_df *df) { struct sc_context *ctx = p15card->card->ctx; unsigned char *buf; const unsigned char *p; size_t bufsize; int r; struct sc_pkcs15_object *obj = NULL; int (* func)(struct sc_pkcs15_card *, struct sc_pkcs15_object *, const u8 **nbuf, size_t *nbufsize) = NULL; sc_log(ctx, "called; path=%s, type=%d, enum=%d", sc_print_path(&df->path), df->type, df->enumerated); if (df->enumerated) LOG_FUNC_RETURN(ctx, SC_SUCCESS); switch (df->type) { case SC_PKCS15_PRKDF: func = sc_pkcs15_decode_prkdf_entry; break; case SC_PKCS15_PUKDF: func = sc_pkcs15_decode_pukdf_entry; break; case SC_PKCS15_SKDF: func = sc_pkcs15_decode_skdf_entry; break; case SC_PKCS15_CDF: case SC_PKCS15_CDF_TRUSTED: case SC_PKCS15_CDF_USEFUL: func = sc_pkcs15_decode_cdf_entry; break; case SC_PKCS15_DODF: func = sc_pkcs15_decode_dodf_entry; break; case SC_PKCS15_AODF: func = sc_pkcs15_decode_aodf_entry; break; } if (func == NULL) { 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, 0); LOG_TEST_RET(ctx, r, "pkcs15 read file failed"); p = buf; while (bufsize && *p != 0x00) { obj = calloc(1, sizeof(struct sc_pkcs15_object)); if (obj == NULL) { r = SC_ERROR_OUT_OF_MEMORY; goto ret; } r = func(p15card, obj, &p, &bufsize); if (r) { free(obj); if (r == SC_ERROR_ASN1_END_OF_CONTENTS) { r = 0; break; } sc_log(ctx, "%s: Error decoding DF entry", sc_strerror(r)); goto ret; } obj->df = df; r = sc_pkcs15_add_object(p15card, obj); if (r) { if (obj->data) free(obj->data); free(obj); sc_log(ctx, "%s: Error adding object", sc_strerror(r)); goto ret; } while (bufsize > 0 && *p == 00) { bufsize--; p++; } }; if (r > 0) r = 0; ret: df->enumerated = 1; free(buf); LOG_FUNC_RETURN(ctx, r); } int sc_pkcs15_add_unusedspace(struct sc_pkcs15_card *p15card, const struct sc_path *path, const struct sc_pkcs15_id *auth_id) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_unusedspace *p = p15card->unusedspace_list, *new_unusedspace; if (path->count == -1) { char pbuf[SC_MAX_PATH_STRING_SIZE]; int r = sc_path_print(pbuf, sizeof(pbuf), path); if (r != SC_SUCCESS) pbuf[0] = '\0'; sc_log(ctx, "No offset and length present in path %s", pbuf); return SC_ERROR_INVALID_ARGUMENTS; } new_unusedspace = calloc(1, sizeof(sc_pkcs15_unusedspace_t)); if (new_unusedspace == NULL) return SC_ERROR_OUT_OF_MEMORY; new_unusedspace->path = *path; if (auth_id != NULL) new_unusedspace->auth_id = *auth_id; if (p15card->unusedspace_list == NULL) { p15card->unusedspace_list = new_unusedspace; return 0; } while (p->next != NULL) p = p->next; p->next = new_unusedspace; new_unusedspace->prev = p; return 0; } void sc_pkcs15_remove_unusedspace(struct sc_pkcs15_card *p15card, struct sc_pkcs15_unusedspace *unusedspace) { if (!unusedspace) return; if (!unusedspace->prev) p15card->unusedspace_list = unusedspace->next; else unusedspace->prev->next = unusedspace->next; if (unusedspace->next) unusedspace->next->prev = unusedspace->prev; free(unusedspace); } static void sc_pkcs15_free_unusedspace(struct sc_pkcs15_card *p15card) { struct sc_pkcs15_unusedspace *cur = NULL, *next = NULL; if (!p15card || !p15card->unusedspace_list) return; for (cur = p15card->unusedspace_list; cur; cur = next) { next = cur->next; free(cur); } p15card->unusedspace_list = NULL; } int sc_pkcs15_encode_unusedspace(struct sc_context *ctx, struct sc_pkcs15_card *p15card, unsigned char **buf, size_t *buflen) { struct sc_path dummy_path; static const struct sc_asn1_entry c_asn1_unusedspace[] = { { "UnusedSpace", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; static const struct sc_asn1_entry c_asn1_unusedspace_values[] = { { "path", SC_ASN1_PATH, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, { "authId", SC_ASN1_PKCS15_ID, SC_ASN1_TAG_OCTET_STRING, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; struct sc_asn1_entry *asn1_unusedspace = NULL; struct sc_asn1_entry *asn1_values = NULL; int unusedspace_count = 0, r, c = 0; struct sc_pkcs15_unusedspace *unusedspace = NULL; sc_format_path("3F00", &dummy_path); dummy_path.index = dummy_path.count = 0; unusedspace = p15card->unusedspace_list; for ( ; unusedspace != NULL; unusedspace = unusedspace->next) unusedspace_count++; if (unusedspace_count == 0) { /* The standard says there has to be at least 1 entry, * so we use a path with a length of 0 bytes */ r = sc_pkcs15_add_unusedspace(p15card, &dummy_path, NULL); if (r) return r; unusedspace_count = 1; } asn1_unusedspace = (struct sc_asn1_entry *) malloc(sizeof(struct sc_asn1_entry) * (unusedspace_count + 1)); if (asn1_unusedspace == NULL) { r = SC_ERROR_OUT_OF_MEMORY; goto err; } asn1_values = (struct sc_asn1_entry *) malloc(sizeof(struct sc_asn1_entry) * (unusedspace_count * 3)); if (asn1_values == NULL) { r = SC_ERROR_OUT_OF_MEMORY; goto err; } for (unusedspace = p15card->unusedspace_list; unusedspace != NULL; unusedspace = unusedspace->next) { sc_copy_asn1_entry(c_asn1_unusedspace, asn1_unusedspace + c); sc_format_asn1_entry(asn1_unusedspace + c, asn1_values + 3*c, NULL, 1); sc_copy_asn1_entry(c_asn1_unusedspace_values, asn1_values + 3*c); sc_format_asn1_entry(asn1_values + 3*c, &unusedspace->path, NULL, 1); sc_format_asn1_entry(asn1_values + 3*c+1, &unusedspace->auth_id, NULL, unusedspace->auth_id.len > 0 ? 1 : 0); c++; } asn1_unusedspace[c].name = NULL; r = sc_asn1_encode(ctx, asn1_unusedspace, buf, buflen); err: if (asn1_values != NULL) free(asn1_values); if (asn1_unusedspace != NULL) free(asn1_unusedspace); /* If we added the dummy entry, remove it now */ if (unusedspace_count == 1 && sc_compare_path(&p15card->unusedspace_list->path, &dummy_path)) sc_pkcs15_remove_unusedspace(p15card, p15card->unusedspace_list); return r; } int sc_pkcs15_parse_unusedspace(const unsigned char *buf, size_t buflen, struct sc_pkcs15_card *p15card) { const unsigned char *p = buf; size_t left = buflen; int r; 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 }, { NULL, 0, 0, 0, NULL, NULL } }; struct sc_asn1_entry asn1_unusedspace_values[] = { { "path", SC_ASN1_PATH, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, { "authId", SC_ASN1_PKCS15_ID, SC_ASN1_TAG_OCTET_STRING, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; /* Clean the list if already present */ sc_pkcs15_free_unusedspace(p15card); 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); while (left > 0) { memset(&auth_id, 0, sizeof(auth_id)); r = sc_asn1_decode(p15card->card->ctx, asn1_unusedspace, p, left, &p, &left); if (r == SC_ERROR_ASN1_END_OF_CONTENTS) break; if (r < 0) return r; /* 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 && p15card->file_app) { r = sc_pkcs15_make_absolute_path(&p15card->file_app->path, &path); if (r < 0) return r; r = sc_pkcs15_add_unusedspace(p15card, &path, &auth_id); if (r) return r; } } p15card->unusedspace_read = 1; 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, int private_data) { struct sc_context *ctx; struct sc_file *file = NULL; unsigned char *data = NULL; size_t len = 0, offset = 0; int r; if (p15card == NULL || p15card->card == NULL || in_path == NULL || buf == NULL) { return SC_ERROR_INVALID_ARGUMENTS; } ctx = p15card->card->ctx; LOG_FUNC_CALLED(ctx); 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 && ((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) { struct sc_path parent = *in_path; parent.len -= 2; parent.type = SC_PATH_TYPE_PATH; r = sc_select_file(p15card->card, &parent, NULL); } } if (r) { r = sc_lock(p15card->card); 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 (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; goto fail_unlock; } if (file->ef_structure == SC_FILE_EF_LINEAR_VARIABLE_TLV) { unsigned int i; size_t l, record_len; unsigned char *head = data; for (i=1; ; i++) { l = len - (head - data); 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) { goto fail_unlock; } if (r < 2) break; record_len = head[1]; if (record_len != 0xff) { memmove(head, head+2, r-2); head += (r-2); } else { if (r < 4) break; 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 { 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 (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); LOG_FUNC_RETURN(ctx, r); } int sc_pkcs15_compare_id(const struct sc_pkcs15_id *id1, const struct sc_pkcs15_id *id2) { if (id1 == NULL || id2 == NULL) return 0; if (id1->len != id2->len) return 0; return memcmp(id1->value, id2->value, id1->len) == 0; } void sc_pkcs15_format_id(const char *str, struct sc_pkcs15_id *id) { size_t len; if (!id) return; len = sizeof(id->value); if (sc_hex_to_bin(str, id->value, &len) != SC_SUCCESS) id->len = 0; else id->len = len; } const char * sc_pkcs15_print_id(const struct sc_pkcs15_id *id) { static char buffer[256]; sc_bin_to_hex(id->value, id->len, buffer, sizeof(buffer), '\0'); return buffer; } int sc_pkcs15_hex_string_to_id(const char *in, struct sc_pkcs15_id *out) { out->len = sizeof(out->value); return sc_hex_to_bin(in, out->value, &out->len); } int sc_pkcs15_make_absolute_path(const struct sc_path *parent, struct sc_path *child) { /* nothing to do if child has valid 'aid' */ if (child->aid.len) return SC_SUCCESS; if (parent->aid.len) { sc_path_t ppath; /* child inherits parent's 'aid' */ child->aid = parent->aid; if (!parent->len) return SC_SUCCESS; /* parent has valid 'path' -- concatenate it with the child's one */ memcpy(&ppath, parent, sizeof(sc_path_t)); ppath.aid.len = 0; ppath.type = SC_PATH_TYPE_FROM_CURRENT; return sc_concatenate_path(child, &ppath, child); } else if (parent->type == SC_PATH_TYPE_DF_NAME) { /* child inherits parent's 'DF NAME' as 'aid' */ if (parent->len > sizeof(child->aid.value)) return SC_ERROR_WRONG_LENGTH; memcpy(child->aid.value, parent->value, parent->len); child->aid.len = parent->len; return SC_SUCCESS; } /* a 0 length path stays a 0 length path */ if (child->len == 0) return SC_SUCCESS; if (sc_compare_path_prefix(sc_get_mf_path(), child)) return SC_SUCCESS; return sc_concatenate_path(child, parent, child); } void sc_pkcs15_free_object_content(struct sc_pkcs15_object *obj) { 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; } int sc_pkcs15_allocate_object_content(struct sc_context *ctx, struct sc_pkcs15_object *obj, const unsigned char *value, size_t len) { unsigned char *tmp_buf; if (!obj) return SC_ERROR_INVALID_ARGUMENTS; if (!value || !len) { sc_pkcs15_free_object_content(obj); return SC_SUCCESS; } /* Need to pass by temporary variable, * because 'value' and 'content.value' pointers can be the sames. */ 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; memcpy(tmp_buf, value, len); sc_pkcs15_free_object_content(obj); obj->content.value = tmp_buf; obj->content.len = len; return SC_SUCCESS; } struct sc_supported_algo_info * sc_pkcs15_get_supported_algo(struct sc_pkcs15_card *p15card, unsigned operation, unsigned mechanism) { struct sc_context *ctx = p15card->card->ctx; struct sc_supported_algo_info *info = NULL; int ii; for (ii=0;iitokeninfo->supported_algos[ii].reference; ii++) if ((p15card->tokeninfo->supported_algos[ii].operations & operation) && (p15card->tokeninfo->supported_algos[ii].mechanism == mechanism)) 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; } 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_t t; if (!ctx || !out) return SC_ERROR_INVALID_ARGUMENTS; *out = NULL; #ifdef HAVE_GETTIMEOFDAY gettimeofday(&tv, NULL); t = tv.tv_sec; #else t = time(NULL); #endif #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)) { free(*out); LOG_TEST_RET(ctx, SC_ERROR_INTERNAL, "strftime failed"); } return SC_SUCCESS; } int sc_pkcs15_add_supported_algo_ref(struct sc_pkcs15_object *obj, struct sc_supported_algo_info *algo) { unsigned int ii, *algo_refs = NULL; if (!algo) return SC_SUCCESS; switch (obj->type & SC_PKCS15_TYPE_CLASS_MASK) { case SC_PKCS15_TYPE_PRKEY: algo_refs = ((struct sc_pkcs15_prkey_info *)obj->data)->algo_refs; break; case SC_PKCS15_TYPE_PUBKEY: algo_refs = ((struct sc_pkcs15_pubkey_info *)obj->data)->algo_refs; break; case SC_PKCS15_TYPE_SKEY: algo_refs = ((struct sc_pkcs15_skey_info *)obj->data)->algo_refs; break; } if (!algo_refs) return SC_ERROR_NOT_SUPPORTED; for (ii=0;iireference) return SC_SUCCESS; for (ii=0;iireference; return SC_SUCCESS; } } return SC_ERROR_TOO_MANY_OBJECTS; } int sc_pkcs15_get_object_id(const struct sc_pkcs15_object *obj, struct sc_pkcs15_id *out) { if (!obj || !out) return SC_ERROR_INVALID_ARGUMENTS; switch (obj->type & SC_PKCS15_TYPE_CLASS_MASK) { case SC_PKCS15_TYPE_CERT: *out = ((struct sc_pkcs15_cert_info *) obj->data)->id; break; case SC_PKCS15_TYPE_PRKEY: *out = ((struct sc_pkcs15_prkey_info *) obj->data)->id; break; case SC_PKCS15_TYPE_PUBKEY: *out = ((struct sc_pkcs15_pubkey_info *) obj->data)->id; break; case SC_PKCS15_TYPE_SKEY: *out = ((struct sc_pkcs15_skey_info *) obj->data)->id; break; case SC_PKCS15_TYPE_AUTH: *out = ((struct sc_pkcs15_auth_info *) obj->data)->auth_id; break; case SC_PKCS15_TYPE_DATA_OBJECT: *out = ((struct sc_pkcs15_data_info *) obj->data)->id; break; default: return SC_ERROR_NOT_SUPPORTED; } return SC_SUCCESS; } /* * Simplified GUID serializing. * Ex. {3F2504E0-4F89-11D3-9A0C-0305E82C3301} * * There is no variant, version number and other special meaning fields * that are described in RFC-4122 . */ int sc_pkcs15_serialize_guid(unsigned char *in, size_t in_size, unsigned flags, char *out, size_t out_size) { int ii, jj, offs = 0; if (in_size < 16) return SC_ERROR_BUFFER_TOO_SMALL; if (out_size < 39) return SC_ERROR_BUFFER_TOO_SMALL; *out = '\0'; if (!flags) strcpy(out, "{"); for (ii=0; ii<4; ii++) sprintf(out + strlen(out), "%02x", *(in + offs++)); for (jj=0; jj<3; jj++) { strcat(out, "-"); for (ii=0; ii<2; ii++) sprintf(out + strlen(out), "%02x", *(in + offs++)); } strcat(out, "-"); for (ii=0; ii<6; ii++) sprintf(out + strlen(out), "%02x", *(in + offs++)); if (!flags) strcat(out, "}"); return SC_SUCCESS; } int sc_pkcs15_get_object_guid(struct sc_pkcs15_card *p15card, const struct sc_pkcs15_object *obj, unsigned flags, unsigned char *out, size_t *out_size) { struct sc_context *ctx = p15card->card->ctx; struct sc_serial_number serialnr; struct sc_pkcs15_id id; unsigned char guid_bin[SC_PKCS15_MAX_ID_SIZE + SC_MAX_SERIALNR]; int rv; size_t guid_bin_size; LOG_FUNC_CALLED(ctx); if(!out || !out_size) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); if (p15card->ops.get_guid) { rv = p15card->ops.get_guid(p15card, obj, out, out_size); LOG_FUNC_RETURN(ctx, rv); } rv = sc_pkcs15_aux_get_md_guid(p15card, obj, flags, out, out_size); if (rv == SC_SUCCESS) LOG_FUNC_RETURN(ctx, SC_SUCCESS); else if (rv != SC_ERROR_NOT_SUPPORTED) LOG_TEST_RET(ctx, rv, "Failed to get alternative object GUID"); memset(out, 0, *out_size); rv = sc_pkcs15_get_object_id(obj, &id); LOG_TEST_RET(ctx, rv, "Cannot get object's ID"); if (p15card->tokeninfo && p15card->tokeninfo->serial_number) { /* The serial from EF(TokenInfo) is preferred because of the * "--serial" parameter of pkcs15-init. */ serialnr.len = SC_MAX_SERIALNR; rv = sc_hex_to_bin(p15card->tokeninfo->serial_number, serialnr.value, &serialnr.len); if (rv) { /* Fallback in case hex_to_bin fails due to unexpected characters */ serialnr.len = strlen(p15card->tokeninfo->serial_number); if (serialnr.len > SC_MAX_SERIALNR) serialnr.len = SC_MAX_SERIALNR; memcpy(serialnr.value, p15card->tokeninfo->serial_number, serialnr.len); } } else if (p15card->card->serialnr.len) { serialnr = p15card->card->serialnr; } else { rv = sc_card_ctl(p15card->card, SC_CARDCTL_GET_SERIALNR, &serialnr); LOG_TEST_RET(ctx, rv, "'GET_SERIALNR' CTL failed and other serial numbers not present"); } memset(guid_bin, 0, sizeof(guid_bin)); memcpy(guid_bin, id.value, id.len); memcpy(guid_bin + id.len, serialnr.value, serialnr.len); guid_bin_size = id.len + serialnr.len; /* * If OpenSSL is available (SHA1), then rather use the hash of the data * - this also protects against data being too short */ #ifdef ENABLE_OPENSSL SHA1(guid_bin, guid_bin_size, guid_bin); guid_bin_size = SHA_DIGEST_LENGTH; #else /* If guid_bin has a size larger than 16 bytes * force the remaining bytes up to 16 bytes to be zero * so sc_pkcs15_serialize_guid won't fail because the size is less than 16 */ if (guid_bin_size < 16) guid_bin_size = 16; #endif rv = sc_pkcs15_serialize_guid(guid_bin, guid_bin_size, flags, (char *)out, *out_size); LOG_TEST_RET(ctx, rv, "Serialize GUID error"); *out_size = strlen((char *)out); LOG_FUNC_RETURN(ctx, rv); } static int sc_pkcs15_aux_get_md_guid(struct sc_pkcs15_card *p15card, const struct sc_pkcs15_object *obj, unsigned flags, unsigned char *out, size_t *out_size) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_prkey_info *prkey_info = NULL; int rv; LOG_FUNC_CALLED(ctx); if(!out || !out_size) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); if ((obj->type & SC_PKCS15_TYPE_CLASS_MASK) != SC_PKCS15_TYPE_PRKEY) LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); prkey_info = (struct sc_pkcs15_prkey_info *)obj->data; if (!prkey_info->aux_data || prkey_info->aux_data->type != SC_AUX_DATA_TYPE_MD_CMAP_RECORD) LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); rv = sc_aux_data_get_md_guid(ctx, prkey_info->aux_data, flags, out, out_size); LOG_FUNC_RETURN(ctx, rv); } void sc_pkcs15_free_key_params(struct sc_pkcs15_key_params *params) { if (!params) return; if (params->data && params->free_params) params->free_params(params->data); else if (params->data) free(params->data); params->data = NULL; } OpenSC-0.26.1/src/libopensc/pkcs15.h000066400000000000000000001104261474147347300167750ustar00rootroot00000000000000/* * pkcs15.h: OpenSC PKCS#15 header file * * Copyright (C) 2001, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _OPENSC_PKCS15_H #define _OPENSC_PKCS15_H #ifdef __cplusplus extern "C" { #endif #include "libopensc/opensc.h" #include "libopensc/aux-data.h" #define SC_PKCS15_CACHE_DIR ".eid" #define SC_PKCS15_PIN_MAGIC 0x31415926 #define SC_PKCS15_MAX_PINS 8 #define SC_PKCS15_MAX_LABEL_SIZE 255 #define SC_PKCS15_MAX_ID_SIZE 255 /* When changing this value, change also initialisation of the * static ASN1 variables, that use this macro, * like for example, 'c_asn1_access_control_rules' * in src/libopensc/asn1.c */ #define SC_PKCS15_MAX_ACCESS_RULES 8 struct sc_pkcs15_id { u8 value[SC_PKCS15_MAX_ID_SIZE]; size_t len; }; typedef struct sc_pkcs15_id sc_pkcs15_id_t; #define SC_PKCS15_CO_FLAG_PRIVATE 0x00000001 #define SC_PKCS15_CO_FLAG_MODIFIABLE 0x00000002 #define SC_PKCS15_CO_FLAG_OBJECT_SEEN 0x80000000 /* for PKCS #11 module */ #define SC_PKCS15_PIN_FLAG_CASE_SENSITIVE 0x0001 #define SC_PKCS15_PIN_FLAG_LOCAL 0x0002 #define SC_PKCS15_PIN_FLAG_CHANGE_DISABLED 0x0004 #define SC_PKCS15_PIN_FLAG_UNBLOCK_DISABLED 0x0008 #define SC_PKCS15_PIN_FLAG_INITIALIZED 0x0010 #define SC_PKCS15_PIN_FLAG_NEEDS_PADDING 0x0020 #define SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN 0x0040 #define SC_PKCS15_PIN_FLAG_SO_PIN 0x0080 #define SC_PKCS15_PIN_FLAG_DISABLE_ALLOW 0x0100 #define SC_PKCS15_PIN_FLAG_INTEGRITY_PROTECTED 0x0200 #define SC_PKCS15_PIN_FLAG_CONFIDENTIALITY_PROTECTED 0x0400 #define SC_PKCS15_PIN_FLAG_EXCHANGE_REF_DATA 0x0800 #define SC_PKCS15_PIN_TYPE_FLAGS_MASK \ ( SC_PKCS15_PIN_FLAG_LOCAL | SC_PKCS15_PIN_FLAG_INITIALIZED \ | SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN | SC_PKCS15_PIN_FLAG_SO_PIN ) #define SC_PKCS15_PIN_TYPE_FLAGS_SOPIN \ ( SC_PKCS15_PIN_FLAG_SO_PIN | SC_PKCS15_PIN_FLAG_INITIALIZED ) #define SC_PKCS15_PIN_TYPE_FLAGS_PIN_GLOBAL \ ( SC_PKCS15_PIN_FLAG_INITIALIZED ) #define SC_PKCS15_PIN_TYPE_FLAGS_PIN_LOCAL \ ( SC_PKCS15_PIN_FLAG_INITIALIZED | SC_PKCS15_PIN_FLAG_LOCAL) #define SC_PKCS15_PIN_TYPE_FLAGS_PUK_GLOBAL \ ( SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN \ | SC_PKCS15_PIN_FLAG_INITIALIZED ) #define SC_PKCS15_PIN_TYPE_FLAGS_PUK_LOCAL \ ( SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN \ | SC_PKCS15_PIN_FLAG_INITIALIZED | SC_PKCS15_PIN_FLAG_LOCAL) #define SC_PKCS15_PIN_TYPE_BCD 0 #define SC_PKCS15_PIN_TYPE_ASCII_NUMERIC 1 #define SC_PKCS15_PIN_TYPE_UTF8 2 #define SC_PKCS15_PIN_TYPE_HALFNIBBLE_BCD 3 #define SC_PKCS15_PIN_TYPE_ISO9564_1 4 #define SC_PKCS15_PIN_AUTH_TYPE_PIN 0 #define SC_PKCS15_PIN_AUTH_TYPE_BIOMETRIC 1 #define SC_PKCS15_PIN_AUTH_TYPE_AUTH_KEY 2 #define SC_PKCS15_PIN_AUTH_TYPE_SM_KEY 3 /* PinAttributes as they defined in PKCS#15 v1.1 for PIN authentication object */ struct sc_pkcs15_pin_attributes { unsigned int flags, type; size_t min_length, stored_length, max_length; int reference; u8 pad_char; }; /* AuthKeyAttributes of the authKey authentication object */ struct sc_pkcs15_authkey_attributes { int derived; struct sc_pkcs15_id skey_id; }; /* BiometricAttributes of the biometricTemplate authentication object */ struct sc_pkcs15_biometric_attributes { unsigned int flags; struct sc_object_id template_id; /* ... */ }; struct sc_pkcs15_auth_info { /* CommonAuthenticationObjectAttributes */ struct sc_pkcs15_id auth_id; /* AuthObjectAttributes */ struct sc_path path; unsigned auth_type; union { struct sc_pkcs15_pin_attributes pin; struct sc_pkcs15_biometric_attributes bio; struct sc_pkcs15_authkey_attributes authkey; } attrs; /* authentication method: CHV, SEN, SYMBOLIC, ... */ unsigned int auth_method; int tries_left, max_tries, logged_in; int max_unlocks; }; typedef struct sc_pkcs15_auth_info sc_pkcs15_auth_info_t; #define SC_PKCS15_ALGO_OP_COMPUTE_CHECKSUM 0x01 #define SC_PKCS15_ALGO_OP_COMPUTE_SIGNATURE 0x02 #define SC_PKCS15_ALGO_OP_VERIFY_CHECKSUM 0x04 #define SC_PKCS15_ALGO_OP_VERIFY_SIGNATURE 0x08 #define SC_PKCS15_ALGO_OP_ENCIPHER 0x10 #define SC_PKCS15_ALGO_OP_DECIPHER 0x20 #define SC_PKCS15_ALGO_OP_HASH 0x40 #define SC_PKCS15_ALGO_OP_GENERATE_KEY 0x80 /* A large integer, big endian notation */ struct sc_pkcs15_bignum { u8 * data; size_t len; }; typedef struct sc_pkcs15_bignum sc_pkcs15_bignum_t; struct sc_pkcs15_der { u8 * value; size_t len; }; typedef struct sc_pkcs15_der sc_pkcs15_der_t; struct sc_pkcs15_u8 { u8 * value; size_t len; }; typedef struct sc_pkcs15_u8 sc_pkcs15_u8_t; struct sc_pkcs15_data { u8 *data; /* DER encoded raw data object */ size_t data_len; }; typedef struct sc_pkcs15_data sc_pkcs15_data_t; #define sc_pkcs15_skey sc_pkcs15_data #define sc_pkcs15_skey_t sc_pkcs15_data_t struct sc_pkcs15_pubkey_rsa { sc_pkcs15_bignum_t modulus; sc_pkcs15_bignum_t exponent; }; struct sc_pkcs15_prkey_rsa { /* public components */ sc_pkcs15_bignum_t modulus; sc_pkcs15_bignum_t exponent; /* private components */ sc_pkcs15_bignum_t d; sc_pkcs15_bignum_t p; sc_pkcs15_bignum_t q; /* optional CRT elements */ sc_pkcs15_bignum_t iqmp; sc_pkcs15_bignum_t dmp1; sc_pkcs15_bignum_t dmq1; }; struct sc_pkcs15_gost_parameters { struct sc_object_id key; struct sc_object_id hash; struct sc_object_id cipher; }; struct sc_pkcs15_pubkey_ec { struct sc_ec_parameters params; struct sc_pkcs15_u8 ecpointQ; /* This is NOT DER, just value and length */ }; struct sc_pkcs15_pubkey_eddsa { struct sc_pkcs15_u8 pubkey; }; 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_prkey_eddsa { struct sc_pkcs15_u8 pubkey; struct sc_pkcs15_u8 value; }; struct sc_pkcs15_pubkey_gostr3410 { struct sc_pkcs15_gost_parameters params; sc_pkcs15_bignum_t xy; }; struct sc_pkcs15_prkey_gostr3410 { struct sc_pkcs15_gost_parameters params; sc_pkcs15_bignum_t d; }; struct sc_pkcs15_pubkey { unsigned long algorithm; struct sc_algorithm_id * alg_id; /* Decoded key */ union { struct sc_pkcs15_pubkey_rsa rsa; struct sc_pkcs15_pubkey_ec ec; struct sc_pkcs15_pubkey_eddsa eddsa; struct sc_pkcs15_pubkey_gostr3410 gostr3410; } u; }; typedef struct sc_pkcs15_pubkey sc_pkcs15_pubkey_t; struct sc_pkcs15_prkey { unsigned long algorithm; /* TODO do we need: struct sc_algorithm_id * alg_id; */ union { struct sc_pkcs15_prkey_rsa rsa; struct sc_pkcs15_prkey_ec ec; struct sc_pkcs15_prkey_eddsa eddsa; struct sc_pkcs15_prkey_gostr3410 gostr3410; struct sc_pkcs15_skey secret; } u; }; typedef struct sc_pkcs15_prkey sc_pkcs15_prkey_t; /* Enveloped objects can be used to provide additional * protection to non-native private keys */ struct sc_pkcs15_enveloped_data { /* recipient info */ sc_pkcs15_id_t id; /* key ID */ struct sc_algorithm_id ke_alg; /* key-encryption algo */ u8 *key; /* encrypted key */ size_t key_len; struct sc_algorithm_id ce_alg; /* content-encryption algo */ u8 *content; /* encrypted content */ size_t content_len; }; struct sc_pkcs15_cert { int version; u8 *serial; size_t serial_len; u8 *issuer; size_t issuer_len; u8 *subject; size_t subject_len; u8 *extensions; size_t extensions_len; struct sc_pkcs15_pubkey * key; /* DER encoded raw cert */ struct sc_pkcs15_der data; }; typedef struct sc_pkcs15_cert sc_pkcs15_cert_t; struct sc_pkcs15_cert_info { struct sc_pkcs15_id id; /* correlates to private key id */ int authority; /* boolean */ /* identifiers [2] SEQUENCE OF CredentialIdentifier{{KeyIdentifiers}} */ struct sc_path path; struct sc_pkcs15_der value; }; typedef struct sc_pkcs15_cert_info sc_pkcs15_cert_info_t; struct sc_pkcs15_data_info { /* FIXME: there is no pkcs15 ID in DataType */ struct sc_pkcs15_id id; /* Identify the application: * either or both may be set */ char app_label[SC_PKCS15_MAX_LABEL_SIZE]; struct sc_object_id app_oid; struct sc_path path; struct sc_pkcs15_der data; }; typedef struct sc_pkcs15_data_info sc_pkcs15_data_info_t; /* keyUsageFlags are the same for all key types */ #define SC_PKCS15_PRKEY_USAGE_ENCRYPT 0x01 #define SC_PKCS15_PRKEY_USAGE_DECRYPT 0x02 #define SC_PKCS15_PRKEY_USAGE_SIGN 0x04 #define SC_PKCS15_PRKEY_USAGE_SIGNRECOVER 0x08 #define SC_PKCS15_PRKEY_USAGE_WRAP 0x10 #define SC_PKCS15_PRKEY_USAGE_UNWRAP 0x20 #define SC_PKCS15_PRKEY_USAGE_VERIFY 0x40 #define SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER 0x80 #define SC_PKCS15_PRKEY_USAGE_DERIVE 0x100 #define SC_PKCS15_PRKEY_USAGE_NONREPUDIATION 0x200 #define SC_PKCS15_PRKEY_ACCESS_SENSITIVE 0x01 #define SC_PKCS15_PRKEY_ACCESS_EXTRACTABLE 0x02 #define SC_PKCS15_PRKEY_ACCESS_ALWAYSSENSITIVE 0x04 #define SC_PKCS15_PRKEY_ACCESS_NEVEREXTRACTABLE 0x08 #define SC_PKCS15_PRKEY_ACCESS_LOCAL 0x10 #define SC_PKCS15_PARAMSET_GOSTR3410_A 1 #define SC_PKCS15_PARAMSET_GOSTR3410_B 2 #define SC_PKCS15_PARAMSET_GOSTR3410_C 3 #define SC_PKCS15_GOSTR3410_KEYSIZE 256 struct sc_pkcs15_keyinfo_gostparams { unsigned int gostr3410, gostr3411, gost28147; }; /* AccessMode bit definitions specified in PKCS#15 v1.1 * and extended by IAS/ECC v1.0.1 specification. */ #define SC_PKCS15_ACCESS_RULE_MODE_READ 0x01 #define SC_PKCS15_ACCESS_RULE_MODE_UPDATE 0x02 #define SC_PKCS15_ACCESS_RULE_MODE_EXECUTE 0x04 #define SC_PKCS15_ACCESS_RULE_MODE_DELETE 0x08 #define SC_PKCS15_ACCESS_RULE_MODE_ATTRIBUTE 0x10 #define SC_PKCS15_ACCESS_RULE_MODE_PSO_CDS 0x20 #define SC_PKCS15_ACCESS_RULE_MODE_PSO_VERIFY 0x40 #define SC_PKCS15_ACCESS_RULE_MODE_PSO_DECRYPT 0x80 #define SC_PKCS15_ACCESS_RULE_MODE_PSO_ENCRYPT 0x100 #define SC_PKCS15_ACCESS_RULE_MODE_INT_AUTH 0x200 #define SC_PKCS15_ACCESS_RULE_MODE_EXT_AUTH 0x400 struct sc_pkcs15_accessrule { unsigned access_mode; struct sc_pkcs15_id auth_id; }; typedef struct sc_pkcs15_accessrule sc_pkcs15_accessrule_t; struct sc_pkcs15_key_params { void *data; size_t len; void (*free_params)(void *); }; struct sc_pkcs15_prkey_info { struct sc_pkcs15_id id; /* correlates to public certificate id */ unsigned int usage, access_flags; int native, key_reference; /* convert to union if other types are supported */ size_t modulus_length; /* RSA, in bits */ size_t field_length; /* EC in bits */ unsigned int algo_refs[SC_MAX_SUPPORTED_ALGORITHMS]; struct sc_pkcs15_der subject; struct sc_pkcs15_key_params params; struct sc_path path; /* Non-pkcs15 data, like MD CMAP record */ struct sc_auxiliary_data *aux_data; }; typedef struct sc_pkcs15_prkey_info sc_pkcs15_prkey_info_t; struct sc_pkcs15_pubkey_info { struct sc_pkcs15_id id; /* correlates to private key id */ unsigned int usage, access_flags; int native, key_reference; /* convert to union if other types are supported */ size_t modulus_length; /* RSA */ size_t field_length; /* EC in bits */ unsigned int algo_refs[SC_MAX_SUPPORTED_ALGORITHMS]; struct sc_pkcs15_der subject; struct sc_pkcs15_key_params params; struct sc_path path; struct { struct sc_pkcs15_der raw; struct sc_pkcs15_der spki; } direct; }; typedef struct sc_pkcs15_pubkey_info sc_pkcs15_pubkey_info_t; struct sc_pkcs15_skey_info { struct sc_pkcs15_id id; unsigned int usage, access_flags; int native, key_reference; size_t value_len; unsigned long key_type; unsigned int algo_refs[SC_MAX_SUPPORTED_ALGORITHMS]; struct sc_path path; /* if on card */ struct sc_pkcs15_der data; }; typedef struct sc_pkcs15_skey_info sc_pkcs15_skey_info_t; #define SC_PKCS15_TYPE_CLASS_MASK 0xF00 #define SC_PKCS15_TYPE_PRKEY 0x100 #define SC_PKCS15_TYPE_PRKEY_RSA 0x101 #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_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 #define SC_PKCS15_TYPE_SKEY_DES 0x302 #define SC_PKCS15_TYPE_SKEY_2DES 0x303 #define SC_PKCS15_TYPE_SKEY_3DES 0x304 #define SC_PKCS15_TYPE_CERT 0x400 #define SC_PKCS15_TYPE_CERT_X509 0x401 #define SC_PKCS15_TYPE_CERT_SPKI 0x402 #define SC_PKCS15_TYPE_DATA_OBJECT 0x500 #define SC_PKCS15_TYPE_AUTH 0x600 #define SC_PKCS15_TYPE_AUTH_PIN 0x601 #define SC_PKCS15_TYPE_AUTH_BIO 0x602 #define SC_PKCS15_TYPE_AUTH_AUTHKEY 0x603 #define SC_PKCS15_TYPE_TO_CLASS(t) (1 << ((t) >> 8)) #define SC_PKCS15_SEARCH_CLASS_PRKEY 0x0002U #define SC_PKCS15_SEARCH_CLASS_PUBKEY 0x0004U #define SC_PKCS15_SEARCH_CLASS_SKEY 0x0008U #define SC_PKCS15_SEARCH_CLASS_CERT 0x0010U #define SC_PKCS15_SEARCH_CLASS_DATA 0x0020U #define SC_PKCS15_SEARCH_CLASS_AUTH 0x0040U struct sc_pkcs15_object { unsigned int type; /* CommonObjectAttributes */ char label[SC_PKCS15_MAX_LABEL_SIZE]; /* zero terminated */ unsigned int flags; struct sc_pkcs15_id auth_id; int usage_counter; int user_consent; struct sc_pkcs15_accessrule access_rules[SC_PKCS15_MAX_ACCESS_RULES]; /* Object type specific data */ void *data; /* emulated object pointer */ void *emulated; struct sc_pkcs15_df *df; /* can be NULL, if object is 'floating' */ 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; /* PKCS #15 DF types */ #define SC_PKCS15_PRKDF 0 #define SC_PKCS15_PUKDF 1 #define SC_PKCS15_PUKDF_TRUSTED 2 #define SC_PKCS15_SKDF 3 #define SC_PKCS15_CDF 4 #define SC_PKCS15_CDF_TRUSTED 5 #define SC_PKCS15_CDF_USEFUL 6 #define SC_PKCS15_DODF 7 #define SC_PKCS15_AODF 8 #define SC_PKCS15_DF_TYPE_COUNT 9 struct sc_pkcs15_card; struct sc_pkcs15_df { struct sc_path path; int record_length; unsigned int type; int enumerated; struct sc_pkcs15_df *next, *prev; }; typedef struct sc_pkcs15_df sc_pkcs15_df_t; struct sc_pkcs15_unusedspace { sc_path_t path; sc_pkcs15_id_t auth_id; struct sc_pkcs15_unusedspace *next, *prev; }; typedef struct sc_pkcs15_unusedspace sc_pkcs15_unusedspace_t; #define SC_PKCS15_CARD_MAGIC 0x10203040 typedef struct sc_pkcs15_sec_env_info { int se; struct sc_object_id owner; struct sc_aid aid; } sc_pkcs15_sec_env_info_t; typedef struct sc_pkcs15_last_update { char *gtime; struct sc_path path; } sc_pkcs15_last_update_t; typedef struct sc_pkcs15_profile_indication { struct sc_object_id oid; char *name; } sc_pkcs15_profile_indication_t; typedef struct sc_pkcs15_tokeninfo { unsigned int version; unsigned int flags; char *label; char *serial_number; char *manufacturer_id; struct sc_pkcs15_last_update last_update; struct sc_pkcs15_profile_indication profile_indication; char *preferred_language; sc_pkcs15_sec_env_info_t **seInfo; size_t num_seInfo; struct sc_supported_algo_info supported_algos[SC_MAX_SUPPORTED_ALGORITHMS]; } sc_pkcs15_tokeninfo_t; struct sc_pkcs15_operations { int (*parse_df)(struct sc_pkcs15_card *, struct sc_pkcs15_df *); void (*clear)(struct sc_pkcs15_card *); int (*get_guid)(struct sc_pkcs15_card *, const struct sc_pkcs15_object *, unsigned char *, size_t *); }; typedef struct sc_pkcs15_card { sc_card_t *card; unsigned int flags; struct sc_app_info *app; sc_file_t *file_app; sc_file_t *file_tokeninfo, *file_odf, *file_unusedspace; struct sc_pkcs15_df *df_list; struct sc_pkcs15_object *obj_list; sc_pkcs15_tokeninfo_t *tokeninfo; sc_pkcs15_unusedspace_t *unusedspace_list; int unusedspace_read; struct sc_pkcs15_card_opts { int use_file_cache; int use_pin_cache; int pin_cache_counter; int pin_cache_ignore_user_consent; int pin_protected_certificate; } opts; unsigned int magic; void *dll_handle; /* shared lib for emulated cards */ struct sc_md_data *md_data; /* minidriver specific data */ struct sc_pkcs15_operations ops; } sc_pkcs15_card_t; /* flags suitable for sc_pkcs15_tokeninfo_t */ #define SC_PKCS15_TOKEN_READONLY 0x01 #define SC_PKCS15_TOKEN_LOGIN_REQUIRED 0x02 /* Don't use */ #define SC_PKCS15_TOKEN_PRN_GENERATION 0x04 #define SC_PKCS15_TOKEN_EID_COMPLIANT 0x08 /* flags suitable for struct sc_pkcs15_card */ #define SC_PKCS15_CARD_FLAG_EMULATED 0x02000000 /* 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 #define SC_X509_DATA_ENCIPHERMENT 0x0008UL #define SC_X509_KEY_AGREEMENT 0x0010UL #define SC_X509_KEY_CERT_SIGN 0x0020UL #define SC_X509_CRL_SIGN 0x0040UL #define SC_X509_ENCIPHER_ONLY 0x0080UL #define SC_X509_DECIPHER_ONLY 0x0100UL /* sc_pkcs15_bind: Binds a card object to a PKCS #15 card object * and initializes a new PKCS #15 card object. Will return * SC_ERROR_PKCS15_APP_NOT_FOUND, if the card hasn't got a * valid PKCS #15 file structure. */ int sc_pkcs15_bind(struct sc_card *card, struct sc_aid *aid, struct sc_pkcs15_card **pkcs15_card); /* sc_pkcs15_unbind: Releases a PKCS #15 card object, and frees any * memory allocations done on the card object. */ int sc_pkcs15_unbind(struct sc_pkcs15_card *card); int sc_pkcs15_bind_internal(struct sc_pkcs15_card *p15card, struct sc_aid *aid); int sc_pkcs15_get_objects(struct sc_pkcs15_card *card, unsigned int type, struct sc_pkcs15_object **ret, size_t ret_count); int sc_pkcs15_get_objects_cond(struct sc_pkcs15_card *card, unsigned int type, int (* func)(struct sc_pkcs15_object *, void *), void *func_arg, struct sc_pkcs15_object **ret, size_t ret_count); int sc_pkcs15_find_object_by_id(struct sc_pkcs15_card *, unsigned int, const sc_pkcs15_id_t *, struct sc_pkcs15_object **); 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, 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, 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, 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 **); 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_gostr3410(struct sc_context *, struct sc_pkcs15_pubkey_gostr3410 *, const u8 *, size_t); int sc_pkcs15_encode_pubkey_gostr3410(struct sc_context *, struct sc_pkcs15_pubkey_gostr3410 *, u8 **, size_t *); 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_eddsa *, 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 *, 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 *); int sc_pkcs15_pubkey_from_prvkey(struct sc_context *, struct sc_pkcs15_prkey *, struct sc_pkcs15_pubkey **); int sc_pkcs15_dup_pubkey(struct sc_context *, struct sc_pkcs15_pubkey *, struct sc_pkcs15_pubkey **); int sc_pkcs15_pubkey_from_cert(struct sc_context *, struct sc_pkcs15_der *, struct sc_pkcs15_pubkey **); int sc_pkcs15_pubkey_from_spki_file(struct sc_context *, char *, struct sc_pkcs15_pubkey ** ); int sc_pkcs15_pubkey_from_spki_fields(struct sc_context *, struct sc_pkcs15_pubkey **, u8 *, size_t, int); 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, struct sc_pkcs15_object **out); int sc_pkcs15_find_data_object_by_app_oid(struct sc_pkcs15_card *p15card, const struct sc_object_id *app_oid, struct sc_pkcs15_object **out); int sc_pkcs15_find_data_object_by_name(struct sc_pkcs15_card *p15card, const char *app_label, const char *label, struct sc_pkcs15_object **out); 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, const struct sc_pkcs15_id *id, struct sc_pkcs15_object **out); 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, u8 **ext_val, size_t *ext_val_len, int *is_critical); int sc_pkcs15_get_bitstring_extension(struct sc_context *ctx, struct sc_pkcs15_cert *cert, const struct sc_object_id *type, 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 * from . has to be NULL-terminated. */ int sc_pkcs15_create_cdf(struct sc_pkcs15_card *card, struct sc_file *file, const struct sc_pkcs15_cert_info **certs); int sc_pkcs15_find_prkey_by_id(struct sc_pkcs15_card *card, const struct sc_pkcs15_id *id, struct sc_pkcs15_object **out); int sc_pkcs15_find_prkey_by_id_usage(struct sc_pkcs15_card *card, const struct sc_pkcs15_id *id, unsigned int usage, struct sc_pkcs15_object **out); int sc_pkcs15_find_prkey_by_reference(struct sc_pkcs15_card *, const sc_path_t *, int, struct sc_pkcs15_object **); int sc_pkcs15_find_pubkey_by_id(struct sc_pkcs15_card *card, const struct sc_pkcs15_id *id, struct sc_pkcs15_object **out); int sc_pkcs15_find_skey_by_id(struct sc_pkcs15_card *card, const struct sc_pkcs15_id *id, struct sc_pkcs15_object **out); int sc_pkcs15_verify_pin(struct sc_pkcs15_card *card, struct sc_pkcs15_object *pin_obj, const u8 *pincode, size_t pinlen); int sc_pkcs15_verify_pin_with_session_pin(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *pin_obj, const unsigned char *pincode, size_t pinlen, const unsigned char *sessionpin, size_t *sessionpinlen); int sc_pkcs15_change_pin(struct sc_pkcs15_card *card, struct sc_pkcs15_object *pin_obj, const u8 *oldpincode, size_t oldpinlen, const u8 *newpincode, size_t newpinlen); int sc_pkcs15_unblock_pin(struct sc_pkcs15_card *card, struct sc_pkcs15_object *pin_obj, const u8 *puk, size_t puklen, const u8 *newpin, size_t newpinlen); int sc_pkcs15_get_pin_info(struct sc_pkcs15_card *card, struct sc_pkcs15_object *pin_obj); int sc_pkcs15_find_pin_by_auth_id(struct sc_pkcs15_card *card, const struct sc_pkcs15_id *id, struct sc_pkcs15_object **out); int sc_pkcs15_find_pin_by_reference(struct sc_pkcs15_card *card, const sc_path_t *path, int reference, struct sc_pkcs15_object **out); int sc_pkcs15_find_pin_by_type_and_reference(struct sc_pkcs15_card *card, const sc_path_t *path, unsigned auth_method, int reference, struct sc_pkcs15_object **out); int sc_pkcs15_find_so_pin(struct sc_pkcs15_card *card, struct sc_pkcs15_object **out); int sc_pkcs15_find_pin_by_flags(struct sc_pkcs15_card *p15card, unsigned flags, unsigned mask, int *index, struct sc_pkcs15_object **out); void sc_pkcs15_pincache_add(struct sc_pkcs15_card *, struct sc_pkcs15_object *, const u8 *, size_t); int sc_pkcs15_pincache_revalidate(struct sc_pkcs15_card *p15card, const struct sc_pkcs15_object *obj); void sc_pkcs15_pincache_clear(struct sc_pkcs15_card *p15card); int sc_pkcs15_encode_dir(struct sc_context *ctx, struct sc_pkcs15_card *card, u8 **buf, size_t *buflen); int sc_pkcs15_parse_tokeninfo(struct sc_context *ctx, sc_pkcs15_tokeninfo_t *ti, const u8 *buf, size_t blen); int sc_pkcs15_encode_tokeninfo(struct sc_context *ctx, sc_pkcs15_tokeninfo_t *ti, u8 **buf, size_t *buflen); int sc_pkcs15_encode_odf(struct sc_context *ctx, struct sc_pkcs15_card *card, u8 **buf, size_t *buflen); int sc_pkcs15_encode_df(struct sc_context *ctx, struct sc_pkcs15_card *p15card, struct sc_pkcs15_df *df, u8 **buf, size_t *bufsize); int sc_pkcs15_encode_cdf_entry(struct sc_context *ctx, const struct sc_pkcs15_object *obj, u8 **buf, size_t *bufsize); int sc_pkcs15_encode_prkdf_entry(struct sc_context *ctx, const struct sc_pkcs15_object *obj, u8 **buf, size_t *bufsize); int sc_pkcs15_encode_pukdf_entry(struct sc_context *ctx, const struct sc_pkcs15_object *obj, u8 **buf, size_t *bufsize); int sc_pkcs15_encode_skdf_entry(struct sc_context *ctx, const struct sc_pkcs15_object *obj, u8 **buf, size_t *buflen); int sc_pkcs15_encode_dodf_entry(struct sc_context *ctx, const struct sc_pkcs15_object *obj, u8 **buf, size_t *bufsize); int sc_pkcs15_encode_aodf_entry(struct sc_context *ctx, const struct sc_pkcs15_object *obj, u8 **buf, size_t *bufsize); int sc_pkcs15_parse_df(struct sc_pkcs15_card *p15card, struct sc_pkcs15_df *df); int sc_pkcs15_read_df(struct sc_pkcs15_card *p15card, struct sc_pkcs15_df *df); int sc_pkcs15_decode_cdf_entry(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *obj, const u8 **buf, size_t *bufsize); int sc_pkcs15_decode_dodf_entry(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *obj, const u8 **buf, size_t *bufsize); int sc_pkcs15_decode_aodf_entry(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *obj, const u8 **buf, size_t *bufsize); int sc_pkcs15_decode_prkdf_entry(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *obj, const u8 **buf, size_t *bufsize); int sc_pkcs15_decode_pukdf_entry(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *obj, const u8 **buf, size_t *bufsize); 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_add_object(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *obj); void sc_pkcs15_remove_object(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *obj); int sc_pkcs15_add_df(struct sc_pkcs15_card *, unsigned int, const sc_path_t *); int sc_pkcs15_add_unusedspace(struct sc_pkcs15_card *p15card, const sc_path_t *path, const sc_pkcs15_id_t *auth_id); int sc_pkcs15_parse_unusedspace(const u8 * buf, size_t buflen, struct sc_pkcs15_card *card); int sc_pkcs15_encode_unusedspace(struct sc_context *ctx, struct sc_pkcs15_card *p15card, u8 **buf, size_t *buflen); /* 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 **); void sc_pkcs15_free_prkey_info(sc_pkcs15_prkey_info_t *key); 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, int private_data); /* Caching functions */ int sc_pkcs15_read_cached_file(struct sc_pkcs15_card *p15card, const struct sc_path *path, u8 **buf, size_t *bufsize); int sc_pkcs15_cache_file(struct sc_pkcs15_card *p15card, const struct sc_path *path, const u8 *buf, size_t bufsize); /* PKCS #15 ID handling functions */ int sc_pkcs15_compare_id(const struct sc_pkcs15_id *id1, const struct sc_pkcs15_id *id2); const char *sc_pkcs15_print_id(const struct sc_pkcs15_id *id); void sc_pkcs15_format_id(const char *id_in, struct sc_pkcs15_id *id_out); int sc_pkcs15_hex_string_to_id(const char *in, struct sc_pkcs15_id *out); int sc_der_copy(struct sc_pkcs15_der *, const struct sc_pkcs15_der *); int sc_pkcs15_get_object_id(const struct sc_pkcs15_object *, struct sc_pkcs15_id *); int sc_pkcs15_get_object_guid(struct sc_pkcs15_card *, const struct sc_pkcs15_object *, unsigned, unsigned char *, size_t *); int sc_pkcs15_serialize_guid(unsigned char *, size_t, unsigned, char *, size_t); int sc_encode_oid (struct sc_context *, struct sc_object_id *, unsigned char **, size_t *); /* Get application by type: 'protected', 'generic' */ struct sc_app_info *sc_pkcs15_get_application_by_type(struct sc_card *, char *); /* Prepend 'parent' to 'child' in case 'child' is a relative path */ int sc_pkcs15_make_absolute_path(const sc_path_t *parent, sc_path_t *child); /* Clean and free object content */ void sc_pkcs15_free_object_content(struct sc_pkcs15_object *); /* Allocate and set object content */ 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 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 *); int sc_pkcs15_fix_ec_parameters(struct sc_context *, struct sc_ec_parameters *); /* Convert the OpenSSL key data type into the OpenSC key */ int sc_pkcs15_convert_bignum(sc_pkcs15_bignum_t *dst, const void *bignum); int sc_pkcs15_convert_prkey(struct sc_pkcs15_prkey *key, void *evp_key); int sc_pkcs15_convert_pubkey(struct sc_pkcs15_pubkey *key, void *evp_key); /* Get 'LastUpdate' string */ char *sc_pkcs15_get_lastupdate(struct sc_pkcs15_card *p15card); /* Allocate generalized time string */ int sc_pkcs15_get_generalized_time(struct sc_context *ctx, char **out); /* New object search API. * More complex, but also more powerful. */ typedef struct sc_pkcs15_search_key { unsigned int class_mask; unsigned int type; const sc_pkcs15_id_t * id; const struct sc_object_id *app_oid; const sc_path_t * path; unsigned int usage_mask, usage_value; unsigned int flags_mask, flags_value; unsigned int match_reference : 1; int reference; const char * app_label; const char * label; } sc_pkcs15_search_key_t; int sc_pkcs15_search_objects(struct sc_pkcs15_card *, sc_pkcs15_search_key_t *, struct sc_pkcs15_object **, size_t); extern int sc_pkcs15_bind_synthetic(struct sc_pkcs15_card *, struct sc_aid *); extern int sc_pkcs15_is_emulation_only(sc_card_t *); int sc_pkcs15emu_object_add(struct sc_pkcs15_card *, unsigned int, const struct sc_pkcs15_object *, const void *); /* some wrapper functions for sc_pkcs15emu_object_add */ int sc_pkcs15emu_add_pin_obj(struct sc_pkcs15_card *, const struct sc_pkcs15_object *, const sc_pkcs15_auth_info_t *); int sc_pkcs15emu_add_rsa_prkey(struct sc_pkcs15_card *, const struct sc_pkcs15_object *, const sc_pkcs15_prkey_info_t *); int sc_pkcs15emu_add_rsa_pubkey(struct sc_pkcs15_card *, const struct sc_pkcs15_object *, const sc_pkcs15_pubkey_info_t *); 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 *, const struct sc_pkcs15_object *, const sc_pkcs15_data_info_t *); #ifdef __cplusplus } #endif #endif OpenSC-0.26.1/src/libopensc/reader-cryptotokenkit.m000066400000000000000000000424761474147347300222360ustar00rootroot00000000000000/* * reader-cryptotokenkit.m: Reader driver for CryptoTokenKit interface * * 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 #ifdef ENABLE_CRYPTOTOKENKIT /* empty file without cryptotokenkit */ #import #include "internal.h" #include "log.h" #include "opensc.h" struct cryptotokenkit_private_data { TKSmartCardSlot* tksmartcardslot; TKSmartCard* tksmartcard; }; static struct sc_reader_operations cryptotokenkit_ops; static struct sc_reader_driver cryptotokenkit_reader_driver = { "CryptoTokenKit pseudo reader", "cryptotokenkit", &cryptotokenkit_ops, 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; } static int cryptotokenkit_release(sc_reader_t *reader) { struct cryptotokenkit_private_data *priv = reader->drv_data; free(priv); return SC_SUCCESS; } static int cryptotokenkit_detect_card_presence(sc_reader_t *reader) { struct cryptotokenkit_private_data *priv = reader->drv_data; int r = SC_SUCCESS; int old_flags = reader->flags; LOG_FUNC_CALLED(reader->ctx); reader->flags &= ~(SC_READER_CARD_INUSE); switch (priv->tksmartcardslot.state) { case TKSmartCardSlotStateMuteCard: // The card inserted in the slot does not answer. r = SC_ERROR_CARD_UNRESPONSIVE; // fall through */ case TKSmartCardSlotStateProbing: // The card was inserted into the slot and an initial probe is in progress. reader->flags |= SC_READER_CARD_INUSE; // fall through */ case TKSmartCardSlotStateValidCard: // Card properly answered to reset. reader->flags |= SC_READER_CARD_PRESENT; if ([priv->tksmartcardslot.ATR.bytes length] > SC_MAX_ATR_SIZE) return SC_ERROR_INTERNAL; reader->atr.len = [priv->tksmartcardslot.ATR.bytes length]; memcpy(reader->atr.value, (unsigned char*) [priv->tksmartcardslot.ATR.bytes bytes], reader->atr.len); break; case TKSmartCardSlotStateMissing: // Slot is no longer known to the system. reader->flags &= ~(SC_READER_CARD_PRESENT); reader->flags |= SC_READER_REMOVED; r = SC_ERROR_READER_DETACHED; break; case TKSmartCardSlotStateEmpty: /// The slot is empty, no card is inserted. reader->flags &= ~SC_READER_CARD_PRESENT; break; default: r = SC_ERROR_UNKNOWN; break; } if ((old_flags & SC_READER_CARD_PRESENT) == (reader->flags & SC_READER_CARD_PRESENT)) reader->flags &= ~SC_READER_CARD_CHANGED; else reader->flags |= SC_READER_CARD_CHANGED; sc_log(reader->ctx, "card %s%s", reader->flags & SC_READER_CARD_PRESENT ? "present" : "absent", reader->flags & SC_READER_CARD_CHANGED ? ", changed": ""); if (r == SC_SUCCESS) r = reader->flags; LOG_FUNC_RETURN(reader->ctx, r); } static int ctk_proto_to_opensc(TKSmartCardProtocol proto) { switch (proto) { case TKSmartCardProtocolT0: return SC_PROTO_T0; case TKSmartCardProtocolT1: /* fall through */ case TKSmartCardProtocolT15: return SC_PROTO_T1; case TKSmartCardProtocolAny: return SC_PROTO_ANY; default: return 0; } } static void ctk_set_proto(sc_reader_t *reader) { struct cryptotokenkit_private_data *priv = reader->drv_data; if (priv->tksmartcard) { reader->active_protocol = ctk_proto_to_opensc(priv->tksmartcard.currentProtocol); if (priv->tksmartcard.allowedProtocols & TKSmartCardProtocolAny) { reader->supported_protocols = ctk_proto_to_opensc(TKSmartCardProtocolAny); } else { if (priv->tksmartcard.allowedProtocols & TKSmartCardProtocolT0) reader->supported_protocols |= ctk_proto_to_opensc(TKSmartCardProtocolT0); if (priv->tksmartcard.allowedProtocols & TKSmartCardProtocolT1) reader->supported_protocols |= ctk_proto_to_opensc(TKSmartCardProtocolT1); if (priv->tksmartcard.allowedProtocols & TKSmartCardProtocolT15) reader->supported_protocols |= ctk_proto_to_opensc(TKSmartCardProtocolT1); } } } static int cryptotokenkit_connect(sc_reader_t *reader) { struct cryptotokenkit_private_data *priv = reader->drv_data; if (!priv->tksmartcard) { priv->tksmartcard = [priv->tksmartcardslot makeSmartCard]; } if (!priv->tksmartcard || !priv->tksmartcard.valid) return SC_ERROR_CARD_NOT_PRESENT; /* 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); return SC_SUCCESS; } static int cryptotokenkit_disconnect(sc_reader_t * reader) { struct cryptotokenkit_private_data *priv = reader->drv_data; priv->tksmartcard = NULL; reader->flags = 0; return SC_SUCCESS; } static int cryptotokenkit_lock(sc_reader_t *reader) { __block int r = SC_ERROR_NOT_ALLOWED; 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) goto err; 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) { r = convertError(error); } else { r = SC_SUCCESS; } dispatch_semaphore_signal(sema); }]; dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); err: LOG_FUNC_RETURN(reader->ctx, r); } static int cryptotokenkit_unlock(sc_reader_t *reader) { struct cryptotokenkit_private_data *priv = reader->drv_data; LOG_FUNC_CALLED(reader->ctx); if (reader->ctx->flags & SC_CTX_FLAG_TERMINATE) return SC_ERROR_NOT_ALLOWED; [priv->tksmartcard endSession]; LOG_FUNC_RETURN(reader->ctx, SC_SUCCESS); } static int cryptotokenkit_transmit(sc_reader_t *reader, sc_apdu_t *apdu) { size_t ssize = 0; __block u8 *rbuf = NULL; __block int r; __block size_t rsize; u8 *sbuf = NULL; struct cryptotokenkit_private_data *priv = reader->drv_data; dispatch_semaphore_t sema = dispatch_semaphore_create(0); LOG_FUNC_CALLED(reader->ctx); 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, sbuf, ssize, 1); [priv->tksmartcard transmitRequest: [NSData dataWithBytes: sbuf length: ssize] reply:^(NSData *response, NSError *error) { if (response) { rsize = [response length]; rbuf = malloc(rsize); if (!rbuf) { r = SC_ERROR_OUT_OF_MEMORY; } else { memcpy(rbuf, (unsigned char*) [response bytes], rsize); r = SC_SUCCESS; } } else { r = convertError(error); } dispatch_semaphore_signal(sema); }]; dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); if (r != SC_SUCCESS) goto err; sc_apdu_log(reader->ctx, rbuf, rsize, 0); r = sc_apdu_set_resp(reader->ctx, apdu, rbuf, rsize); err: if (sbuf != NULL) { sc_mem_clear(sbuf, ssize); free(sbuf); } if (rbuf != NULL) { sc_mem_clear(rbuf, rsize); free(rbuf); } 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; /* 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; struct cryptotokenkit_private_data *priv; sc_reader_t *reader = NULL; scconf_block *conf_block = NULL; TKSmartCardSlot* tksmartcardslot = (__bridge TKSmartCardSlot *)(pcsc_context_handle); TKSmartCard* tksmartcard = (__bridge TKSmartCard *)(pcsc_card_handle); const char* utf8String; if (!pcsc_context_handle) { if (!pcsc_card_handle) return SC_ERROR_INVALID_ARGUMENTS; tksmartcardslot = tksmartcard.slot; } if ((reader = calloc(1, sizeof(sc_reader_t))) == NULL) { r = SC_ERROR_OUT_OF_MEMORY; goto err; } if ((priv = calloc(1, sizeof(struct cryptotokenkit_private_data))) == NULL) { r = SC_ERROR_OUT_OF_MEMORY; goto err; } [priv->tksmartcard autorelease]; priv->tksmartcard = [tksmartcard retain]; [priv->tksmartcardslot autorelease]; priv->tksmartcardslot = [tksmartcardslot retain]; reader->drv_data = priv; reader->ops = &cryptotokenkit_ops; reader->driver = &cryptotokenkit_reader_driver; utf8String = [tksmartcardslot.name UTF8String]; if ((reader->name = strdup(utf8String)) == NULL) { r = SC_ERROR_OUT_OF_MEMORY; goto err; } /* By testing we found that maxInputLength/maxOutputLength are * most likely initialized badly. We still take this value as is * and leave it up to the user to overwrite the reader's * capabilities */ reader->max_send_size = tksmartcardslot.maxInputLength; reader->max_recv_size = tksmartcardslot.maxOutputLength; conf_block = sc_get_conf_block(ctx, "reader_driver", "cryptotokenkit", 1); if (conf_block) { reader->max_send_size = scconf_get_int(conf_block, "max_send_size", reader->max_send_size); reader->max_recv_size = scconf_get_int(conf_block, "max_recv_size", reader->max_recv_size); } /* 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: if (r != SC_SUCCESS) { free(priv); if (reader != NULL) { free(reader->name); free(reader->vendor); free(reader); } } return r; } 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); if (!mngr) { /* com.apple.security.smartcard entitlement is disabled */ r = SC_ERROR_NOT_ALLOWED; goto err; } sc_log(ctx, "Probing CryptoTokenKit readers"); slotNames = [[mngr slotNames] mutableCopy]; /* 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; } for (j = 0; j < [slotNames count]; j++) { if (!strcmp(reader->name, [slotNames[j] UTF8String])) break; } 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); }]; dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); } r = SC_SUCCESS; err: LOG_FUNC_RETURN(ctx, r); } struct sc_reader_driver *sc_get_cryptotokenkit_driver(void) { cryptotokenkit_ops.init = cryptotokenkit_init; cryptotokenkit_ops.finish = NULL; cryptotokenkit_ops.release = cryptotokenkit_release; cryptotokenkit_ops.detect_card_presence = cryptotokenkit_detect_card_presence; cryptotokenkit_ops.connect = cryptotokenkit_connect; cryptotokenkit_ops.disconnect = cryptotokenkit_disconnect; cryptotokenkit_ops.lock = cryptotokenkit_lock; cryptotokenkit_ops.unlock = cryptotokenkit_unlock; cryptotokenkit_ops.transmit = cryptotokenkit_transmit; 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; return &cryptotokenkit_reader_driver; } #endif OpenSC-0.26.1/src/libopensc/reader-ctapi.c000066400000000000000000000353751474147347300202330ustar00rootroot00000000000000/* * reader-ctapi.c: Reader driver for CT-API * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef ENABLE_CTAPI #include #include #include #include "common/libscdl.h" #include "internal.h" #include "ctbcs.h" #define GET_PRIV_DATA(r) ((struct ctapi_private_data *) (r)->drv_data) #ifdef _WIN32 typedef char pascal CT_INIT_TYPE(unsigned short ctn, unsigned short Pn); typedef char pascal CT_CLOSE_TYPE(unsigned short ctn); typedef char pascal CT_DATA_TYPE(unsigned short ctn, unsigned char *dad, \ unsigned char *sad, unsigned short lc, \ unsigned char *cmd, unsigned short *lr, \ unsigned char *rsp); #else typedef char CT_INIT_TYPE(unsigned short ctn, unsigned short Pn); typedef char CT_CLOSE_TYPE(unsigned short ctn); typedef char CT_DATA_TYPE(unsigned short ctn, unsigned char *dad, \ unsigned char *sad, unsigned short lc, \ unsigned char *cmd, unsigned short *lr, \ unsigned char *rsp); #endif struct ctapi_module { char *name; void *dlhandle; int ctn_count; }; struct ctapi_global_private_data { int module_count; struct ctapi_module *modules; }; struct ctapi_functions { CT_INIT_TYPE *CT_init; CT_CLOSE_TYPE *CT_close; CT_DATA_TYPE *CT_data; }; /* Reader specific private data */ #define CTAPI_FU_KEYBOARD 0x1 #define CTAPI_FU_DISPLAY 0x2 #define CTAPI_FU_BIOMETRIC 0x4 #define CTAPI_FU_PRINTER 0x8 struct ctapi_private_data { struct ctapi_functions funcs; unsigned short ctn; int ctapi_functional_units; int slot; }; /* Reset reader */ static int ctapi_reset(sc_reader_t *reader) { struct ctapi_private_data *priv = GET_PRIV_DATA(reader); char rv; u8 cmd[5], rbuf[256], sad, dad; unsigned short lr; cmd[0] = CTBCS_CLA; cmd[1] = CTBCS_INS_RESET; cmd[2] = priv->slot ? CTBCS_P1_INTERFACE1 + priv->slot : CTBCS_P1_CT_KERNEL; cmd[3] = 0x00; /* No response. We might also use 0x01 (return ATR) or 0x02 (return historical bytes) here */ cmd[4] = 0x00; dad = 1; sad = 2; lr = 256; rv = priv->funcs.CT_data(priv->ctn, &dad, &sad, 5, cmd, &lr, rbuf); if (rv || (lr < 2)) { sc_log(reader->ctx, "Error getting status of terminal: %d, using defaults", rv); return SC_ERROR_TRANSMIT_FAILED; } if (rbuf[lr-2] != 0x90) { sc_log(reader->ctx, "SW1/SW2: 0x%x/0x%x", rbuf[lr-2], rbuf[lr-1]); return SC_ERROR_TRANSMIT_FAILED; } return 0; } static int refresh_attributes(sc_reader_t *reader) { struct ctapi_private_data *priv = GET_PRIV_DATA(reader); char rv; u8 cmd[5], rbuf[256], sad, dad; unsigned short lr; if (reader->ctx->flags & SC_CTX_FLAG_TERMINATE) return SC_ERROR_NOT_ALLOWED; cmd[0] = CTBCS_CLA; cmd[1] = CTBCS_INS_STATUS; cmd[2] = CTBCS_P1_CT_KERNEL; cmd[3] = CTBCS_P2_STATUS_ICC; cmd[4] = 0x00; dad = 1; sad = 2; lr = 256; reader->flags = 0; rv = priv->funcs.CT_data(priv->ctn, &dad, &sad, 5, cmd, &lr, rbuf); if (rv || (lr < 3) || (rbuf[lr-2] != 0x90)) { sc_log(reader->ctx, "Error getting status of terminal: %d/%d/0x%x", rv, lr, rbuf[lr-2]); return SC_ERROR_TRANSMIT_FAILED; } if (lr < 4) { if (rbuf[0] & CTBCS_DATA_STATUS_CARD) reader->flags = SC_READER_CARD_PRESENT; } 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 returned on CTBCS_P2_STATUS_ICC: 0x%x", rbuf[0]); return SC_ERROR_TRANSMIT_FAILED; } /* Fixme - should not be reached */ sc_log(reader->ctx, "Returned status for %d slots", rbuf[1]); reader->flags = SC_READER_CARD_PRESENT; } return 0; } static int ctapi_internal_transmit(sc_reader_t *reader, const u8 *sendbuf, size_t sendsize, u8 *recvbuf, size_t *recvsize, unsigned long control) { struct ctapi_private_data *priv = GET_PRIV_DATA(reader); u8 dad, sad; unsigned short lr; char rv; if (reader->ctx->flags & SC_CTX_FLAG_TERMINATE) return SC_ERROR_NOT_ALLOWED; if (control) dad = 1; else dad = 0; sad = 2; lr = *recvsize; rv = priv->funcs.CT_data(priv->ctn, &dad, &sad, (unsigned short)sendsize, (u8 *) sendbuf, &lr, recvbuf); if (rv != 0) { sc_log(reader->ctx, "Error transmitting APDU: %d", rv); return SC_ERROR_TRANSMIT_FAILED; } *recvsize = lr; return 0; } static int ctapi_transmit(sc_reader_t *reader, sc_apdu_t *apdu) { size_t ssize, rsize, rbuflen = 0; u8 *sbuf = NULL, *rbuf = NULL; int r; rsize = rbuflen = apdu->resplen + 2; rbuf = malloc(rbuflen); if (rbuf == NULL) { r = SC_ERROR_OUT_OF_MEMORY; goto out; } /* encode and log the 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, sbuf, ssize, 1); r = ctapi_internal_transmit(reader, sbuf, ssize, rbuf, &rsize, apdu->control); if (r < 0) { /* unable to transmit ... most likely a reader problem */ sc_log(reader->ctx, "unable to transmit"); goto out; } sc_apdu_log(reader->ctx, rbuf, rsize, 0); /* set response */ r = sc_apdu_set_resp(reader->ctx, apdu, rbuf, rsize); out: if (sbuf != NULL) { sc_mem_clear(sbuf, ssize); free(sbuf); } if (rbuf != NULL) { sc_mem_clear(rbuf, rbuflen); free(rbuf); } return r; } static int ctapi_detect_card_presence(sc_reader_t *reader) { int r; r = refresh_attributes(reader); if (r) return r; return reader->flags; } static int ctapi_connect(sc_reader_t *reader) { struct ctapi_private_data *priv = GET_PRIV_DATA(reader); char rv; u8 cmd[9], rbuf[256], sad, dad; unsigned short lr; if (reader->ctx->flags & SC_CTX_FLAG_TERMINATE) return SC_ERROR_NOT_ALLOWED; cmd[0] = CTBCS_CLA; cmd[1] = CTBCS_INS_REQUEST; cmd[2] = CTBCS_P1_INTERFACE1; cmd[3] = CTBCS_P2_REQUEST_GET_ATR; cmd[4] = 0x00; dad = 1; sad = 2; lr = 256; rv = priv->funcs.CT_data(priv->ctn, &dad, &sad, 5, cmd, &lr, rbuf); if (rv || rbuf[lr-2] != 0x90) { sc_log(reader->ctx, "Error activating card: %d", rv); return SC_ERROR_TRANSMIT_FAILED; } if (lr < 2) 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); _sc_parse_atr(reader); return 0; } static int ctapi_disconnect(sc_reader_t *reader) { return 0; } static int ctapi_lock(sc_reader_t *reader) { return 0; } static int ctapi_unlock(sc_reader_t *reader) { return 0; } static int ctapi_release(sc_reader_t *reader) { struct ctapi_private_data *priv = GET_PRIV_DATA(reader); if (!(reader->ctx->flags & SC_CTX_FLAG_TERMINATE)) priv->funcs.CT_close(priv->ctn); free(priv); return 0; } static struct sc_reader_operations ctapi_ops; static struct sc_reader_driver ctapi_drv = { "CT-API module", "ctapi", &ctapi_ops, NULL }; static struct ctapi_module * add_module(struct ctapi_global_private_data *gpriv, const char *name, void *dlhandle) { int i; struct ctapi_module *p; i = gpriv->module_count; p = (struct ctapi_module *) realloc(gpriv->modules, sizeof(struct ctapi_module) * (i+1)); if (!p) { return NULL; } gpriv->modules = p; gpriv->modules[i].name = strdup(name); gpriv->modules[i].dlhandle = dlhandle; gpriv->modules[i].ctn_count = 0; gpriv->module_count++; return &gpriv->modules[i]; } static int ctapi_load_module(sc_context_t *ctx, struct ctapi_global_private_data *gpriv, scconf_block *conf) { const char *val; struct ctapi_functions funcs; struct ctapi_module *mod; const scconf_list *list; scconf_block *conf_block = NULL; void *dlh; int r, i, NumUnits; u8 cmd[5], rbuf[256], sad, dad; unsigned short lr; list = scconf_find_list(conf, "ports"); if (list == NULL) { sc_log(ctx, "No ports configured."); return -1; } val = conf->name->data; dlh = sc_dlopen(val); if (!dlh) { sc_log(ctx, "Unable to open shared library '%s': %s", val, sc_dlerror()); return -1; } funcs.CT_init = (CT_INIT_TYPE *) sc_dlsym(dlh, "CT_init"); if (!funcs.CT_init) goto symerr; funcs.CT_close = (CT_CLOSE_TYPE *) sc_dlsym(dlh, "CT_close"); if (!funcs.CT_close) goto symerr; funcs.CT_data = (CT_DATA_TYPE *) sc_dlsym(dlh, "CT_data"); if (!funcs.CT_data) goto symerr; mod = add_module(gpriv, val, dlh); if (!mod) goto symerr; for (; list != NULL; list = list->next) { int port; char namebuf[128]; char rv; sc_reader_t *reader; struct ctapi_private_data *priv; if (sscanf(list->data, "%d", &port) != 1) { sc_log(ctx, "Port '%s' is not a number.", list->data); continue; } rv = funcs.CT_init((unsigned short)mod->ctn_count, (unsigned short)port); if (rv) { sc_log(ctx, "CT_init() failed with %d", rv); continue; } reader = calloc(1, sizeof(sc_reader_t)); priv = calloc(1, sizeof(struct ctapi_private_data)); if (!priv || !reader) { free(reader); free(priv); return SC_ERROR_OUT_OF_MEMORY; } reader->drv_data = priv; reader->ops = &ctapi_ops; reader->driver = &ctapi_drv; snprintf(namebuf, sizeof(namebuf), "CT-API %s, port %d", mod->name, port); reader->name = strdup(namebuf); priv->funcs = funcs; priv->ctn = mod->ctn_count; reader->max_send_size = SC_READER_SHORT_APDU_MAX_SEND_SIZE; reader->max_recv_size = SC_READER_SHORT_APDU_MAX_RECV_SIZE; conf_block = sc_get_conf_block(ctx, "reader_driver", "ctapi", 1); if (conf_block) { reader->max_send_size = scconf_get_int(conf_block, "max_send_size", reader->max_send_size); reader->max_recv_size = scconf_get_int(conf_block, "max_recv_size", reader->max_recv_size); if (scconf_get_bool(conf_block, "enable_escape", 0)) reader->flags |= SC_READER_ENABLE_ESCAPE; } r = _sc_add_reader(ctx, reader); if (r) { funcs.CT_close((unsigned short)mod->ctn_count); free(priv); free(reader->name); free(reader); break; } /* Detect functional units of the reader according to CT-BCS spec version 1.0 (14.04.2004, http://www.teletrust.de/down/mct1-0_t4.zip) */ cmd[0] = CTBCS_CLA; cmd[1] = CTBCS_INS_STATUS; cmd[2] = CTBCS_P1_CT_KERNEL; cmd[3] = CTBCS_P2_STATUS_TFU; cmd[4] = 0x00; dad = 1; sad = 2; lr = 256; rv = priv->funcs.CT_data(priv->ctn, &dad, &sad, 5, cmd, &lr, rbuf); if (rv || (lr < 4) || (rbuf[lr-2] != 0x90)) { sc_log(reader->ctx, "Error getting status of terminal: %d, using defaults", rv); } 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 returned on CTBCS_P2_STATUS_TFU: 0x%x", rbuf[0]); } NumUnits = rbuf[1]; if (NumUnits + 4 > lr) { 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++) { switch(rbuf[i+2]) { case CTBCS_P1_INTERFACE1: case CTBCS_P1_INTERFACE2: case CTBCS_P1_INTERFACE3: case CTBCS_P1_INTERFACE4: case CTBCS_P1_INTERFACE5: case CTBCS_P1_INTERFACE6: case CTBCS_P1_INTERFACE7: case CTBCS_P1_INTERFACE8: case CTBCS_P1_INTERFACE9: case CTBCS_P1_INTERFACE10: case CTBCS_P1_INTERFACE11: case CTBCS_P1_INTERFACE12: case CTBCS_P1_INTERFACE13: case CTBCS_P1_INTERFACE14: /* Maybe a weak point here if multiple interfaces are present and not returned in the "canonical" order. This is not forbidden by the specs, but why should anyone want to do that? */ sc_log(reader->ctx, "Found slot id 0x%x", rbuf[i+2]); break; case CTBCS_P1_DISPLAY: priv->ctapi_functional_units |= CTAPI_FU_DISPLAY; sc_log(reader->ctx, "Display detected"); break; case CTBCS_P1_KEYPAD: priv->ctapi_functional_units |= CTAPI_FU_KEYBOARD; sc_log(reader->ctx, "Keypad detected"); break; case CTBCS_P1_PRINTER: priv->ctapi_functional_units |= CTAPI_FU_PRINTER; sc_log(reader->ctx, "Printer detected"); break; case CTBCS_P1_FINGERPRINT: case CTBCS_P1_VOICEPRINT: case CTBCS_P1_DSV: case CTBCS_P1_FACE_RECOGNITION: case CTBCS_P1_IRISSCAN: priv->ctapi_functional_units |= CTAPI_FU_BIOMETRIC; sc_log(reader->ctx, "Biometric sensor detected"); break; default: sc_log(reader->ctx, "Unknown functional unit 0x%x", rbuf[i+2]); } } /* CT-BCS does not define Keyboard/Display for each slot, so I assume those additional units can be used for each slot */ if (priv->ctapi_functional_units) { if (priv->ctapi_functional_units & CTAPI_FU_KEYBOARD) reader->capabilities |= SC_READER_CAP_PIN_PAD; if (priv->ctapi_functional_units & CTAPI_FU_DISPLAY) reader->capabilities |= SC_READER_CAP_DISPLAY; } ctapi_reset(reader); refresh_attributes(reader); mod->ctn_count++; } return 0; symerr: sc_log(ctx, "Unable to resolve CT-API symbols."); sc_dlclose(dlh); return -1; } static int ctapi_init(sc_context_t *ctx) { int i; struct ctapi_global_private_data *gpriv; scconf_block **blocks = NULL, *conf_block = NULL; gpriv = calloc(1, sizeof(struct ctapi_global_private_data)); if (gpriv == NULL) return SC_ERROR_OUT_OF_MEMORY; ctx->reader_drv_data = gpriv; conf_block = sc_get_conf_block(ctx, "reader_driver", "ctapi", 1); if (conf_block) { blocks = scconf_find_blocks(ctx->conf, conf_block, "module", NULL); for (i = 0; blocks != NULL && blocks[i] != NULL; i++) ctapi_load_module(ctx, gpriv, blocks[i]); free(blocks); } return 0; } static int ctapi_finish(sc_context_t *ctx) { struct ctapi_global_private_data *priv = (struct ctapi_global_private_data *) ctx->reader_drv_data; if (priv) { int i; for (i = 0; i < priv->module_count; i++) { struct ctapi_module *mod = &priv->modules[i]; free(mod->name); sc_dlclose(mod->dlhandle); } if (priv->module_count) free(priv->modules); free(priv); } return 0; } struct sc_reader_driver * sc_get_ctapi_driver(void) { ctapi_ops.init = ctapi_init; ctapi_ops.finish = ctapi_finish; ctapi_ops.detect_readers = NULL; ctapi_ops.transmit = ctapi_transmit; ctapi_ops.detect_card_presence = ctapi_detect_card_presence; ctapi_ops.lock = ctapi_lock; ctapi_ops.unlock = ctapi_unlock; ctapi_ops.release = ctapi_release; ctapi_ops.connect = ctapi_connect; ctapi_ops.disconnect = ctapi_disconnect; ctapi_ops.perform_verify = ctbcs_pin_cmd; ctapi_ops.perform_pace = NULL; ctapi_ops.use_reader = NULL; return &ctapi_drv; } #endif OpenSC-0.26.1/src/libopensc/reader-openct.c000066400000000000000000000307261474147347300204160ustar00rootroot00000000000000/* * reader-openct.c: backend for OpenCT * * 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 #ifdef ENABLE_OPENCT /* empty file without openct */ #include #include #include #include #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include "internal.h" /* function declarations */ static int openct_reader_init(sc_context_t *ctx); static int openct_add_reader(sc_context_t *ctx, unsigned int num, ct_info_t *info); static int openct_reader_finish(sc_context_t *ctx); static int openct_reader_release(sc_reader_t *reader); static int openct_reader_detect_card_presence(sc_reader_t *reader); static int openct_reader_connect(sc_reader_t *reader); static int openct_reader_disconnect(sc_reader_t *reader); static int openct_reader_transmit(sc_reader_t *reader, sc_apdu_t *apdu); static int openct_reader_perform_verify(sc_reader_t *reader, struct sc_pin_cmd_data *info); static int openct_reader_lock(sc_reader_t *reader); static int openct_reader_unlock(sc_reader_t *reader); static int openct_error(sc_reader_t *, int); static struct sc_reader_operations openct_ops; static struct sc_reader_driver openct_reader_driver = { "OpenCT reader", "openct", &openct_ops, NULL }; /* private data structures */ struct driver_data { ct_handle * h; unsigned int num; ct_info_t info; ct_lock_handle excl_lock; ct_lock_handle shared_lock; unsigned int slot; }; /* * Initialize readers * * Called during sc_establish_context(), when the driver * is loaded */ static int openct_reader_init(sc_context_t *ctx) { unsigned int i,max_virtual; scconf_block *conf_block; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); max_virtual = 2; conf_block = sc_get_conf_block(ctx, "reader_driver", "openct", 1); if (conf_block) { max_virtual = scconf_get_int(conf_block, "readers", max_virtual); } for (i = 0; i < OPENCT_MAX_READERS; i++) { ct_info_t info; /* XXX: As long as OpenCT has slots, multislot readers should create several instances here. */ if (ct_reader_info(i, &info) >= 0) { openct_add_reader(ctx, i, &info); } else if (i < max_virtual) { openct_add_reader(ctx, i, NULL); } } return SC_SUCCESS; } static int openct_add_reader(sc_context_t *ctx, unsigned int num, ct_info_t *info) { sc_reader_t *reader; scconf_block *conf_block; struct driver_data *data; int rc; if (!(reader = calloc(1, sizeof(*reader))) || !(data = (calloc(1, sizeof(*data))))) { free(reader); return SC_ERROR_OUT_OF_MEMORY; } if (info) { data->info = *info; } else { strcpy(data->info.ct_name, "OpenCT reader (detached)"); data->info.ct_slots = 1; } data->num = num; reader->driver = &openct_reader_driver; reader->ops = &openct_ops; reader->drv_data = data; reader->name = strdup(data->info.ct_name); conf_block = sc_get_conf_block(ctx, "reader_driver", "openct", 1); if (conf_block) { reader->max_send_size = scconf_get_int(conf_block, "max_send_size", reader->max_send_size); reader->max_recv_size = scconf_get_int(conf_block, "max_recv_size", reader->max_recv_size); if (scconf_get_bool(conf_block, "enable_escape", 0)) reader->flags |= SC_READER_ENABLE_ESCAPE; } if ((rc = _sc_add_reader(ctx, reader)) < 0) { free(data); free(reader->name); free(reader); return rc; } if (data->info.ct_display) reader->capabilities |= SC_READER_CAP_DISPLAY; if (data->info.ct_keypad) reader->capabilities |= SC_READER_CAP_PIN_PAD; return 0; } /* * Called when the driver is being unloaded. finish() has to * deallocate the private data and any resources. */ static int openct_reader_finish(sc_context_t *ctx) { SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); return SC_SUCCESS; } /* * Called when releasing a reader. release() has to * deallocate the private data. Other fields will be * freed by OpenSC. */ static int openct_reader_release(sc_reader_t *reader) { struct driver_data *data = (struct driver_data *) reader->drv_data; SC_FUNC_CALLED(reader->ctx, SC_LOG_DEBUG_VERBOSE); if (data) { if (data->h && !(reader->ctx->flags & SC_CTX_FLAG_TERMINATE)) ct_reader_disconnect(data->h); sc_mem_clear(data, sizeof(*data)); reader->drv_data = NULL; free(data); } return SC_SUCCESS; } /* * Check whether a card was added/removed */ static int openct_reader_detect_card_presence(sc_reader_t *reader) { struct driver_data *data = (struct driver_data *) reader->drv_data; int rc, status; SC_FUNC_CALLED(reader->ctx, SC_LOG_DEBUG_VERBOSE); if (reader->ctx->flags & SC_CTX_FLAG_TERMINATE) return SC_ERROR_NOT_ALLOWED; reader->flags = 0; if (!data->h && !(data->h = ct_reader_connect(data->num))) return 0; if ((rc = ct_card_status(data->h, data->slot, &status)) < 0) return SC_ERROR_TRANSMIT_FAILED; if (status & IFD_CARD_PRESENT) { reader->flags = SC_READER_CARD_PRESENT; if (status & IFD_CARD_STATUS_CHANGED) reader->flags = SC_READER_CARD_PRESENT; } return reader->flags; } static int openct_reader_connect(sc_reader_t *reader) { struct driver_data *data = (struct driver_data *) reader->drv_data; int rc; SC_FUNC_CALLED(reader->ctx, SC_LOG_DEBUG_VERBOSE); if (reader->ctx->flags & SC_CTX_FLAG_TERMINATE) return SC_ERROR_NOT_ALLOWED; if (data->h) ct_reader_disconnect(data->h); if (!(data->h = ct_reader_connect(data->num))) { 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_log(reader->ctx, "openct_reader_connect read failed: %s\n", ct_strerror(rc)); return SC_ERROR_CARD_NOT_PRESENT; } if (rc == 0) { sc_log(reader->ctx, "openct_reader_connect received no data\n"); return SC_ERROR_READER; } reader->atr.len = rc; return SC_SUCCESS; } static int openct_reader_reconnect(sc_reader_t *reader) { struct driver_data *data = (struct driver_data *) reader->drv_data; int rc; if (data->h != NULL) return 0; if ((rc = openct_reader_connect(reader)) < 0) return SC_ERROR_READER_DETACHED; return SC_ERROR_READER_REATTACHED; } static int openct_reader_disconnect(sc_reader_t *reader) { struct driver_data *data = (struct driver_data *) reader->drv_data; SC_FUNC_CALLED(reader->ctx, SC_LOG_DEBUG_VERBOSE); if (data->h && !(reader->ctx->flags & SC_CTX_FLAG_TERMINATE)) ct_reader_disconnect(data->h); data->h = NULL; return SC_SUCCESS; } static int openct_reader_internal_transmit(sc_reader_t *reader, const u8 *sendbuf, size_t sendsize, u8 *recvbuf, size_t *recvsize, unsigned long control) { struct driver_data *data = (struct driver_data *) reader->drv_data; int rc; if (reader->ctx->flags & SC_CTX_FLAG_TERMINATE) return SC_ERROR_NOT_ALLOWED; /* Hotplug check */ if ((rc = openct_reader_reconnect(reader)) < 0) return rc; rc = ct_card_transact(data->h, data->slot, sendbuf, sendsize, recvbuf, *recvsize); if (rc == IFD_ERROR_NOT_CONNECTED) { ct_reader_disconnect(data->h); data->h = NULL; return SC_ERROR_READER_DETACHED; } if (rc >= 0) *recvsize = rc; return openct_error(reader, rc); } static int openct_reader_transmit(sc_reader_t *reader, sc_apdu_t *apdu) { size_t ssize, rsize, rbuflen = 0; u8 *sbuf = NULL, *rbuf = NULL; int r; rsize = rbuflen = apdu->resplen + 2; rbuf = malloc(rbuflen); if (rbuf == NULL) { r = SC_ERROR_OUT_OF_MEMORY; goto out; } /* encode and log the 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, 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_log(reader->ctx, "unable to transmit"); goto out; } sc_apdu_log(reader->ctx, rbuf, rsize, 0); /* set response */ r = sc_apdu_set_resp(reader->ctx, apdu, rbuf, rsize); out: if (sbuf != NULL) { sc_mem_clear(sbuf, ssize); free(sbuf); } if (rbuf != NULL) { sc_mem_clear(rbuf, rbuflen); free(rbuf); } return r; } static int openct_reader_perform_verify(sc_reader_t *reader, struct sc_pin_cmd_data *info) { struct driver_data *data = (struct driver_data *) reader->drv_data; unsigned int pin_length = 0, pin_encoding; size_t j = 0; u8 buf[254]; int rc; if (reader->ctx->flags & SC_CTX_FLAG_TERMINATE) return SC_ERROR_NOT_ALLOWED; /* Hotplug check */ if ((rc = openct_reader_reconnect(reader)) < 0) return rc; if (info->apdu == NULL) { /* complain */ return SC_ERROR_INVALID_ARGUMENTS; } buf[j++] = info->apdu->cla; buf[j++] = info->apdu->ins; buf[j++] = info->apdu->p1; buf[j++] = info->apdu->p2; if (info->apdu->lc) { size_t len = info->apdu->lc; if (j + 1 + len > sizeof(buf)) return SC_ERROR_BUFFER_TOO_SMALL; buf[j++] = len; memcpy(buf+j, info->apdu->data, len); j += len; } if (info->pin1.min_length == info->pin1.max_length) pin_length = info->pin1.min_length; if (info->pin1.encoding == SC_PIN_ENCODING_ASCII) pin_encoding = IFD_PIN_ENCODING_ASCII; else if (info->pin1.encoding == SC_PIN_ENCODING_BCD) pin_encoding = IFD_PIN_ENCODING_BCD; else return SC_ERROR_INVALID_ARGUMENTS; rc = ct_card_verify(data->h, data->slot, 0, /* no timeout?! */ info->pin1.prompt, pin_encoding, pin_length, info->pin1.offset, buf, j, buf, sizeof(buf)); if (rc < 0) return openct_error(reader, rc); if (rc != 2) return SC_ERROR_UNKNOWN_DATA_RECEIVED; info->apdu->sw1 = buf[0]; info->apdu->sw2 = buf[1]; return 0; } static int openct_reader_lock(sc_reader_t *reader) { struct driver_data *data = (struct driver_data *) reader->drv_data; int rc; SC_FUNC_CALLED(reader->ctx, SC_LOG_DEBUG_VERBOSE); if (reader->ctx->flags & SC_CTX_FLAG_TERMINATE) return SC_ERROR_NOT_ALLOWED; /* Hotplug check */ if ((rc = openct_reader_reconnect(reader)) < 0) return rc; rc = ct_card_lock(data->h, data->slot, IFD_LOCK_EXCLUSIVE, &data->excl_lock); if (rc == IFD_ERROR_NOT_CONNECTED) { ct_reader_disconnect(data->h); data->h = NULL; /* Try to reconnect as reader may be plugged-in again */ return openct_reader_reconnect(reader); } return openct_error(reader, rc); } static int openct_reader_unlock(sc_reader_t *reader) { struct driver_data *data = (struct driver_data *) reader->drv_data; int rc; SC_FUNC_CALLED(reader->ctx, SC_LOG_DEBUG_VERBOSE); if (reader->ctx->flags & SC_CTX_FLAG_TERMINATE) return SC_ERROR_NOT_ALLOWED; /* Not connected */ if (data->h == NULL) return 0; rc = ct_card_unlock(data->h, data->slot, data->excl_lock); /* We couldn't care less */ if (rc == IFD_ERROR_NOT_CONNECTED) return 0; return openct_error(reader, rc); } /* * Handle an error code returned by OpenCT */ static int openct_error(sc_reader_t *reader, int code) { if (code >= 0) return code; /* Fixme: translate error code */ switch (code) { case IFD_ERROR_USER_TIMEOUT: return SC_ERROR_KEYPAD_TIMEOUT; case IFD_ERROR_USER_ABORT: return SC_ERROR_KEYPAD_CANCELLED; } return SC_ERROR_READER; } struct sc_reader_driver *sc_get_openct_driver(void) { openct_ops.init = openct_reader_init; openct_ops.finish = openct_reader_finish; openct_ops.detect_readers = NULL; openct_ops.release = openct_reader_release; openct_ops.detect_card_presence = openct_reader_detect_card_presence; openct_ops.connect = openct_reader_connect; openct_ops.disconnect = openct_reader_disconnect; openct_ops.transmit = openct_reader_transmit; openct_ops.perform_verify = openct_reader_perform_verify; openct_ops.perform_pace = NULL; openct_ops.lock = openct_reader_lock; openct_ops.unlock = openct_reader_unlock; openct_ops.use_reader = NULL; return &openct_reader_driver; } #endif /* ENABLE_OPENCT */ OpenSC-0.26.1/src/libopensc/reader-pcsc.c000066400000000000000000002344331474147347300200570ustar00rootroot00000000000000/* * reader-pcsc.c: Reader driver for PC/SC interface * * Copyright (C) 2002 Juha Yrjölä * Copyright (C) 2009,2010 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 #ifdef ENABLE_PCSC /* empty file without pcsc */ #include #include #include #include #ifdef _WIN32 #include #else #include #endif #include "common/libscdl.h" #include "internal.h" #include "internal-winscard.h" #include "card-sc-hsm.h" #include "pace.h" #ifdef HAVE_PCSCLITE_H #if !defined (__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED < 101000 #define HAVE_PCSCLITE 1 #endif #endif #define SCARD_CLASS_SYSTEM 0x7fff #define SCARD_ATTR_VALUE(Class, Tag) ((((ULONG)(Class)) << 16) | ((ULONG)(Tag))) #ifndef SCARD_ATTR_DEVICE_FRIENDLY_NAME_A #define SCARD_ATTR_DEVICE_FRIENDLY_NAME_A SCARD_ATTR_VALUE(SCARD_CLASS_SYSTEM, 0x0003) #endif #ifndef SCARD_ATTR_DEVICE_SYSTEM_NAME_A #define SCARD_ATTR_DEVICE_SYSTEM_NAME_A SCARD_ATTR_VALUE(SCARD_CLASS_SYSTEM, 0x0004) #endif #define SCARD_CLASS_VENDOR_INFO 1 #ifndef SCARD_ATTR_VENDOR_NAME #define SCARD_ATTR_VENDOR_NAME SCARD_ATTR_VALUE(SCARD_CLASS_VENDOR_INFO, 0x0100) /**< Vendor name. */ #endif #ifndef SCARD_ATTR_VENDOR_IFD_TYPE #define SCARD_ATTR_VENDOR_IFD_TYPE SCARD_ATTR_VALUE(SCARD_CLASS_VENDOR_INFO, 0x0101) /**< Vendor-supplied interface device type (model designation of reader). */ #endif #ifndef SCARD_ATTR_VENDOR_IFD_VERSION #define SCARD_ATTR_VENDOR_IFD_VERSION SCARD_ATTR_VALUE(SCARD_CLASS_VENDOR_INFO, 0x0102) /**< Vendor-supplied interface device version (DWORD in the form 0xMMmmbbbb where MM = major version, mm = minor version, and bbbb = build number). */ #endif /* Logging */ #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 struct pcsc_global_private_data { int cardmod; SCARDCONTEXT pcsc_ctx; SCARDCONTEXT pcsc_wait_ctx; int enable_pinpad; int fixed_pinlength; int enable_pace; size_t force_max_recv_size; size_t force_max_send_size; int connect_exclusive; DWORD disconnect_action; DWORD transaction_end_action; DWORD reconnect_action; const char *provider_library; void *dlhandle; SCardEstablishContext_t SCardEstablishContext; SCardReleaseContext_t SCardReleaseContext; SCardConnect_t SCardConnect; SCardReconnect_t SCardReconnect; SCardDisconnect_t SCardDisconnect; SCardBeginTransaction_t SCardBeginTransaction; SCardEndTransaction_t SCardEndTransaction; SCardStatus_t SCardStatus; SCardGetStatusChange_t SCardGetStatusChange; SCardCancel_t SCardCancel; SCardControlOLD_t SCardControlOLD; SCardControl_t SCardControl; SCardTransmit_t SCardTransmit; SCardListReaders_t SCardListReaders; SCardGetAttrib_t SCardGetAttrib; sc_reader_t *attached_reader; sc_reader_t *removed_reader; }; struct pcsc_private_data { struct pcsc_global_private_data *gpriv; SCARDHANDLE pcsc_card; SCARD_READERSTATE reader_state; DWORD verify_ioctl; DWORD verify_ioctl_start; DWORD verify_ioctl_finish; DWORD modify_ioctl; DWORD modify_ioctl_start; DWORD modify_ioctl_finish; DWORD pace_ioctl; DWORD pin_properties_ioctl; DWORD get_tlv_properties; int locked; }; 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) { if (!strcmp(str, "reset")) return SCARD_RESET_CARD; else if (!strcmp(str, "unpower")) return SCARD_UNPOWER_CARD; else return SCARD_LEAVE_CARD; } static int pcsc_to_opensc_error(LONG rv) { switch (rv) { case SCARD_S_SUCCESS: return SC_SUCCESS; case SCARD_W_REMOVED_CARD: return SC_ERROR_CARD_REMOVED; case SCARD_E_NOT_TRANSACTED: return SC_ERROR_TRANSMIT_FAILED; case SCARD_W_UNRESPONSIVE_CARD: return SC_ERROR_CARD_UNRESPONSIVE; case SCARD_W_UNPOWERED_CARD: 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: return SC_ERROR_CARD_NOT_PRESENT; case SCARD_E_PROTO_MISMATCH: /* Should not happen */ return SC_ERROR_READER; default: return SC_ERROR_UNKNOWN; } } static unsigned int pcsc_proto_to_opensc(DWORD proto) { switch (proto) { case SCARD_PROTOCOL_T0: return SC_PROTO_T0; case SCARD_PROTOCOL_T1: return SC_PROTO_T1; case SCARD_PROTOCOL_RAW: return SC_PROTO_RAW; default: return 0; } } static DWORD opensc_proto_to_pcsc(unsigned int proto) { switch (proto) { case SC_PROTO_T0: return SCARD_PROTOCOL_T0; case SC_PROTO_T1: return SCARD_PROTOCOL_T1; case SC_PROTO_RAW: return SCARD_PROTOCOL_RAW; default: return 0; } } static int pcsc_internal_transmit(sc_reader_t *reader, const u8 *sendbuf, size_t sendsize, u8 *recvbuf, size_t *recvsize, unsigned long control) { struct pcsc_private_data *priv = reader->drv_data; SCARD_IO_REQUEST sSendPci, sRecvPci; DWORD dwSendLength, dwRecvLength; LONG rv = SCARD_E_INVALID_VALUE; int r; SCARDHANDLE card; LOG_FUNC_CALLED(reader->ctx); card = priv->pcsc_card; if (reader->ctx->flags & SC_CTX_FLAG_TERMINATE) return SC_ERROR_NOT_ALLOWED; sSendPci.dwProtocol = opensc_proto_to_pcsc(reader->active_protocol); sSendPci.cbPciLength = sizeof(sSendPci); sRecvPci.dwProtocol = opensc_proto_to_pcsc(reader->active_protocol); sRecvPci.cbPciLength = sizeof(sRecvPci); dwSendLength = sendsize; dwRecvLength = *recvsize; if (!control) { rv = priv->gpriv->SCardTransmit(card, &sSendPci, sendbuf, dwSendLength, &sRecvPci, recvbuf, &dwRecvLength); } else { if (priv->gpriv->SCardControlOLD) { rv = priv->gpriv->SCardControlOLD(card, sendbuf, dwSendLength, recvbuf, &dwRecvLength); } else if (priv->gpriv->SCardControl) { rv = priv->gpriv->SCardControl(card, (DWORD)control, sendbuf, dwSendLength, recvbuf, dwRecvLength, &dwRecvLength); } } if (rv != SCARD_S_SUCCESS) { PCSC_TRACE(reader, "SCardTransmit/Control failed", rv); 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); if (!(pcsc_detect_card_presence(reader) & SC_READER_CARD_PRESENT)) return SC_ERROR_CARD_REMOVED; return SC_ERROR_TRANSMIT_FAILED; } } if (!control && dwRecvLength < 2) return SC_ERROR_UNKNOWN_DATA_RECEIVED; *recvsize = dwRecvLength; return SC_SUCCESS; } static int pcsc_transmit(sc_reader_t *reader, sc_apdu_t *apdu) { size_t ssize, rsize, rbuflen = 0; u8 *sbuf = NULL, *rbuf = NULL; int r; /* we always use a at least 258 byte size big return buffer * to mimic the behaviour of the old implementation (some readers * seems to require a larger than necessary return buffer). * 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); if (rbuf == NULL) { r = SC_ERROR_OUT_OF_MEMORY; goto out; } /* encode and log the APDU */ r = sc_apdu_get_octets(reader->ctx, apdu, &sbuf, &ssize, reader->active_protocol); if (r != SC_SUCCESS) goto out; if (reader->name) sc_log(reader->ctx, "reader '%s'", reader->name); sc_apdu_log(reader->ctx, sbuf, ssize, 1); r = pcsc_internal_transmit(reader, sbuf, ssize, rbuf, &rsize, apdu->control); if (r < 0) { /* unable to transmit ... most likely a reader problem */ sc_log(reader->ctx, "unable to transmit"); goto out; } 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); out: if (sbuf != NULL) { sc_mem_clear(sbuf, ssize); free(sbuf); } if (rbuf != NULL) { sc_mem_clear(rbuf, rbuflen); free(rbuf); } return r; } /* Calls SCardGetStatusChange on the reader to set ATR and associated flags * (card present/changed) */ static int refresh_attributes(sc_reader_t *reader) { struct pcsc_private_data *priv = reader->drv_data; unsigned long old_flags = reader->flags; DWORD state, prev_state; LONG rv; sc_log(reader->ctx, "%s check", reader->name); if (reader->ctx->flags & SC_CTX_FLAG_TERMINATE) return SC_ERROR_NOT_ALLOWED; 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; } else { priv->reader_state.dwCurrentState = priv->reader_state.dwEventState; } rv = priv->gpriv->SCardGetStatusChange(priv->gpriv->pcsc_ctx, 0, &priv->reader_state, 1); 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. */ reader->flags &= ~SC_READER_CARD_CHANGED; /* 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; } 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) { 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); } state = priv->reader_state.dwEventState; prev_state = priv->reader_state.dwCurrentState; sc_log(reader->ctx, "current state: 0x%08X", (unsigned int)state); sc_log(reader->ctx, "previous state: 0x%08X", (unsigned int)prev_state); if (state & SCARD_STATE_UNKNOWN) { /* State means "reader unknown", but we have listed it at least once. * There can be no cards in this reader. * XXX: We'll hit it again, as no readers are removed currently. */ reader->flags &= ~(SC_READER_CARD_PRESENT); 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); if (state & SCARD_STATE_PRESENT) { reader->flags |= SC_READER_CARD_PRESENT; 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 */ 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 ? */ if (state & SCARD_STATE_INUSE) reader->flags |= SC_READER_CARD_INUSE; if (state & SCARD_STATE_EXCLUSIVE) reader->flags |= SC_READER_CARD_EXCLUSIVE; if (old_flags & SC_READER_CARD_PRESENT) { /* Requires pcsc-lite 1.6.5+ to function properly */ if ((state & 0xFFFF0000) != (prev_state & 0xFFFF0000)) { reader->flags |= SC_READER_CARD_CHANGED; } else { /* Check if the card handle is still valid. If the card changed, * the handle will be invalid. */ DWORD readers_len = 0, cstate, prot, atr_len = SC_MAX_ATR_SIZE; 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 || rv == (LONG)SCARD_E_INVALID_VALUE) reader->flags |= SC_READER_CARD_CHANGED; } } else { reader->flags |= SC_READER_CARD_CHANGED; } } else { reader->flags &= ~SC_READER_CARD_PRESENT; } sc_log(reader->ctx, "card %s%s", reader->flags & SC_READER_CARD_PRESENT ? "present" : "absent", reader->flags & SC_READER_CARD_CHANGED ? ", changed": ""); return SC_SUCCESS; } static int pcsc_detect_card_presence(sc_reader_t *reader) { int rv; LOG_FUNC_CALLED(reader->ctx); rv = refresh_attributes(reader); if (rv != SC_SUCCESS) LOG_FUNC_RETURN(reader->ctx, rv); // 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 forced = 0; atrblock = _sc_match_atr_block(reader->ctx, NULL, &reader->atr); if (atrblock != NULL) { const char *forcestr; forcestr = scconf_get_str(atrblock, "force_protocol", "unknown"); if (!strcmp(forcestr, "t0")) { *protocol = SCARD_PROTOCOL_T0; forced = 1; } else if (!strcmp(forcestr, "t1")) { *protocol = SCARD_PROTOCOL_T1; forced = 1; } else if (!strcmp(forcestr, "raw")) { *protocol = SCARD_PROTOCOL_RAW; forced = 1; } if (forced) sc_log(reader->ctx, "force_protocol: %s", forcestr); } 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; 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 forced; } static int pcsc_reconnect(sc_reader_t * reader, DWORD action) { DWORD active_proto = opensc_proto_to_pcsc(reader->active_protocol), tmp, protocol = SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1; LONG rv; struct pcsc_private_data *priv = reader->drv_data; int r; sc_log(reader->ctx, "Reconnecting to the card..."); r = refresh_attributes(reader); if (r!= SC_SUCCESS) return r; if (!(reader->flags & SC_READER_CARD_PRESENT)) return SC_ERROR_CARD_NOT_PRESENT; /* Check if we need a specific protocol. refresh_attributes above already sets the ATR */ if (check_forced_protocol(reader, &tmp)) protocol = tmp; #ifndef HAVE_PCSCLITE /* reconnect unlocks transaction everywhere but in PCSC-lite */ priv->locked = 0; #endif rv = priv->gpriv->SCardReconnect(priv->pcsc_card, 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); return pcsc_to_opensc_error(rv); } reader->active_protocol = pcsc_proto_to_opensc(active_proto); return pcsc_to_opensc_error(rv); } static void initialize_uid(sc_reader_t *reader) { if (reader->flags & SC_READER_ENABLE_ESCAPE) { sc_apdu_t apdu; /* though we only expect 10 bytes max, we want to set the Le to 0x00 to not * get 0x6282 as SW in case of a UID variant shorter than 10 bytes */ u8 rbuf[256]; memset(&apdu, 0, sizeof(apdu)); apdu.cse = SC_APDU_CASE_2_SHORT; apdu.cla = 0xFF; apdu.ins = 0xCA; apdu.p1 = 0x00; apdu.p2 = 0x00; apdu.le = 0x00; apdu.resp = rbuf; apdu.resplen = sizeof rbuf; if (SC_SUCCESS == pcsc_transmit(reader, &apdu) && 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_log_hex(reader->ctx, "UID", reader->uid.value, reader->uid.len); } else { sc_log(reader->ctx, "unable to get UID"); } } } static int pcsc_connect(sc_reader_t *reader) { DWORD active_proto, tmp, protocol = SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1; SCARDHANDLE card_handle; LONG rv; struct pcsc_private_data *priv = reader->drv_data; int r; LOG_FUNC_CALLED(reader->ctx); r = refresh_attributes(reader); if (r != SC_SUCCESS) LOG_FUNC_RETURN(reader->ctx, r); if (!(reader->flags & SC_READER_CARD_PRESENT)) LOG_FUNC_RETURN(reader->ctx, SC_ERROR_CARD_NOT_PRESENT); if (!priv->gpriv->cardmod) { 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); } reader->active_protocol = pcsc_proto_to_opensc(active_proto); priv->pcsc_card = card_handle; initialize_uid(reader); sc_log(reader->ctx, "Initial protocol: %s", reader->active_protocol == SC_PROTO_T1 ? "T=1" : "T=0"); /* Check if we need a specific protocol. refresh_attributes above already sets the ATR */ if (check_forced_protocol(reader, &tmp)) { if (active_proto != tmp) { sc_log(reader->ctx, "Reconnecting to force protocol"); r = pcsc_reconnect(reader, SCARD_UNPOWER_CARD); if (r != SC_SUCCESS) { sc_log(reader->ctx, "pcsc_reconnect (to force protocol) failed (%d)", r); return r; } } sc_log(reader->ctx, "Final protocol: %s", reader->active_protocol == SC_PROTO_T1 ? "T=1" : "T=0"); } } else { initialize_uid(reader); } /* After connect reader is not locked yet */ priv->locked = 0; return SC_SUCCESS; } static int pcsc_disconnect(sc_reader_t * reader) { struct pcsc_private_data *priv = reader->drv_data; 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); } reader->flags &= SC_READER_REMOVED; return SC_SUCCESS; } static int pcsc_lock(sc_reader_t *reader) { LONG rv; int r; struct pcsc_private_data *priv = reader->drv_data; if (priv->gpriv->cardmod) return SC_SUCCESS; LOG_FUNC_CALLED(reader->ctx); if (reader->ctx->flags & SC_CTX_FLAG_TERMINATE) return SC_ERROR_NOT_ALLOWED; rv = priv->gpriv->SCardBeginTransaction(priv->pcsc_card); if (rv != SCARD_S_SUCCESS) 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); 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: 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 and try to lock again */ return SC_ERROR_CARD_RESET; case SCARD_S_SUCCESS: priv->locked = 1; return SC_SUCCESS; default: PCSC_TRACE(reader, "SCardBeginTransaction failed", rv); return pcsc_to_opensc_error(rv); } } static int pcsc_unlock(sc_reader_t *reader) { LONG rv; struct pcsc_private_data *priv = reader->drv_data; if (priv->gpriv->cardmod) return SC_SUCCESS; LOG_FUNC_CALLED(reader->ctx); if (reader->ctx->flags & SC_CTX_FLAG_TERMINATE) return SC_ERROR_NOT_ALLOWED; rv = priv->gpriv->SCardEndTransaction(priv->pcsc_card, priv->gpriv->transaction_end_action); priv->locked = 0; if (rv != SCARD_S_SUCCESS) { PCSC_TRACE(reader, "SCardEndTransaction failed", rv); return pcsc_to_opensc_error(rv); } return SC_SUCCESS; } static int pcsc_release(sc_reader_t *reader) { struct pcsc_private_data *priv = reader->drv_data; free(priv); return SC_SUCCESS; } static int pcsc_reset(sc_reader_t *reader, int do_cold_reset) { int r; #ifndef HAVE_PCSCLITE struct pcsc_private_data *priv = reader->drv_data; int old_locked = priv->locked; #endif r = pcsc_reconnect(reader, do_cold_reset ? SCARD_UNPOWER_CARD : SCARD_RESET_CARD); if(r != SC_SUCCESS) return r; #ifndef HAVE_PCSCLITE /* reconnect unlocks transaction everywhere but in PCSC-lite */ if(old_locked) r = pcsc_lock(reader); #endif return r; } static int pcsc_cancel(sc_context_t *ctx) { LONG rv = SCARD_S_SUCCESS; struct pcsc_global_private_data *gpriv = (struct pcsc_global_private_data *)ctx->reader_drv_data; LOG_FUNC_CALLED(ctx); if (ctx->flags & SC_CTX_FLAG_TERMINATE) return SC_ERROR_NOT_ALLOWED; #ifndef _WIN32 if (gpriv->pcsc_wait_ctx != (SCARDCONTEXT)-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; } } #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); } return SC_SUCCESS; } static struct sc_reader_operations pcsc_ops; static struct sc_reader_driver pcsc_drv = { "PC/SC reader", "pcsc", &pcsc_ops, NULL }; static int pcsc_init(sc_context_t *ctx) { struct pcsc_global_private_data *gpriv; scconf_block *conf_block = NULL; int ret = SC_ERROR_INTERNAL; gpriv = calloc(1, sizeof(struct pcsc_global_private_data)); if (gpriv == NULL) { ret = SC_ERROR_OUT_OF_MEMORY; goto out; } if(strcmp(ctx->app_name, "cardmod") == 0) { gpriv->cardmod = 1; } /* PC/SC Defaults */ gpriv->provider_library = DEFAULT_PCSC_PROVIDER; gpriv->connect_exclusive = 0; 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; /* 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; gpriv->force_max_recv_size = 0; conf_block = sc_get_conf_block(ctx, "reader_driver", "pcsc", 1); if (conf_block) { gpriv->provider_library = scconf_get_str(conf_block, "provider_library", gpriv->provider_library); 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", "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", (int)gpriv->force_max_send_size); gpriv->force_max_recv_size = scconf_get_int(conf_block, "max_recv_size", (int)gpriv->force_max_recv_size); } if (gpriv->cardmod) { /* for cardmod, don't manipulate winscard.dll or the OS's builtin * management of SCARDHANDLEs */ gpriv->provider_library = DEFAULT_PCSC_PROVIDER; gpriv->connect_exclusive = 0; gpriv->disconnect_action = SCARD_LEAVE_CARD; gpriv->transaction_end_action = SCARD_LEAVE_CARD; gpriv->reconnect_action = SCARD_LEAVE_CARD; } sc_log(ctx, "PC/SC options: connect_exclusive=%d disconnect_action=%u transaction_end_action=%u" " reconnect_action=%u enable_pinpad=%d enable_pace=%d", gpriv->connect_exclusive, (unsigned int)gpriv->disconnect_action, (unsigned int)gpriv->transaction_end_action, (unsigned int)gpriv->reconnect_action, gpriv->enable_pinpad, gpriv->enable_pace); gpriv->dlhandle = sc_dlopen(gpriv->provider_library); if (gpriv->dlhandle == NULL) { ret = SC_ERROR_CANNOT_LOAD_MODULE; goto out; } gpriv->SCardEstablishContext = (SCardEstablishContext_t)sc_dlsym(gpriv->dlhandle, "SCardEstablishContext"); gpriv->SCardReleaseContext = (SCardReleaseContext_t)sc_dlsym(gpriv->dlhandle, "SCardReleaseContext"); gpriv->SCardConnect = (SCardConnect_t)sc_dlsym(gpriv->dlhandle, "SCardConnect"); gpriv->SCardReconnect = (SCardReconnect_t)sc_dlsym(gpriv->dlhandle, "SCardReconnect"); gpriv->SCardDisconnect = (SCardDisconnect_t)sc_dlsym(gpriv->dlhandle, "SCardDisconnect"); gpriv->SCardBeginTransaction = (SCardBeginTransaction_t)sc_dlsym(gpriv->dlhandle, "SCardBeginTransaction"); gpriv->SCardEndTransaction = (SCardEndTransaction_t)sc_dlsym(gpriv->dlhandle, "SCardEndTransaction"); gpriv->SCardStatus = (SCardStatus_t)sc_dlsym(gpriv->dlhandle, "SCardStatus"); gpriv->SCardGetStatusChange = (SCardGetStatusChange_t)sc_dlsym(gpriv->dlhandle, "SCardGetStatusChange"); gpriv->SCardCancel = (SCardCancel_t)sc_dlsym(gpriv->dlhandle, "SCardCancel"); gpriv->SCardTransmit = (SCardTransmit_t)sc_dlsym(gpriv->dlhandle, "SCardTransmit"); gpriv->SCardListReaders = (SCardListReaders_t)sc_dlsym(gpriv->dlhandle, "SCardListReaders"); if (gpriv->SCardConnect == NULL) gpriv->SCardConnect = (SCardConnect_t)sc_dlsym(gpriv->dlhandle, "SCardConnectA"); if (gpriv->SCardStatus == NULL) gpriv->SCardStatus = (SCardStatus_t)sc_dlsym(gpriv->dlhandle, "SCardStatusA"); if (gpriv->SCardGetStatusChange == NULL) gpriv->SCardGetStatusChange = (SCardGetStatusChange_t)sc_dlsym(gpriv->dlhandle, "SCardGetStatusChangeA"); if (gpriv->SCardListReaders == NULL) gpriv->SCardListReaders = (SCardListReaders_t)sc_dlsym(gpriv->dlhandle, "SCardListReadersA"); /* If we have SCardGetAttrib it is correct API */ gpriv->SCardGetAttrib = (SCardGetAttrib_t)sc_dlsym(gpriv->dlhandle, "SCardGetAttrib"); if (gpriv->SCardGetAttrib != NULL) { #ifdef __APPLE__ gpriv->SCardControl = (SCardControl_t)sc_dlsym(gpriv->dlhandle, "SCardControl132"); #endif if (gpriv->SCardControl == NULL) { gpriv->SCardControl = (SCardControl_t)sc_dlsym(gpriv->dlhandle, "SCardControl"); } } else { gpriv->SCardControlOLD = (SCardControlOLD_t)sc_dlsym(gpriv->dlhandle, "SCardControl"); } if ( gpriv->SCardReleaseContext == NULL || gpriv->SCardConnect == NULL || gpriv->SCardReconnect == NULL || gpriv->SCardDisconnect == NULL || gpriv->SCardBeginTransaction == NULL || gpriv->SCardEndTransaction == NULL || gpriv->SCardStatus == NULL || gpriv->SCardGetStatusChange == NULL || gpriv->SCardCancel == NULL || (gpriv->SCardControl == NULL && gpriv->SCardControlOLD == NULL) || gpriv->SCardTransmit == NULL || gpriv->SCardListReaders == NULL ) { ret = SC_ERROR_CANNOT_LOAD_MODULE; goto out; } ctx->reader_drv_data = gpriv; gpriv = NULL; ret = SC_SUCCESS; out: if (gpriv != NULL) { if (gpriv->dlhandle != NULL) sc_dlclose(gpriv->dlhandle); free(gpriv); } return ret; } static int pcsc_finish(sc_context_t *ctx) { struct pcsc_global_private_data *gpriv = (struct pcsc_global_private_data *) ctx->reader_drv_data; LOG_FUNC_CALLED(ctx); if (gpriv) { if (!gpriv->cardmod && gpriv->pcsc_ctx != (SCARDCONTEXT)-1 && !(ctx->flags & SC_CTX_FLAG_TERMINATE)) gpriv->SCardReleaseContext(gpriv->pcsc_ctx); if (gpriv->dlhandle != NULL) sc_dlclose(gpriv->dlhandle); free(gpriv); } return SC_SUCCESS; } /** * @brief Detects reader's PACE capabilities * * @param reader reader to probe (\c pace_ioctl must be initialized) * * @return Bitmask of \c SC_READER_CAP_PACE_GENERIC, \c SC_READER_CAP_PACE_EID and \c * SC_READER_CAP_PACE_ESIGN logically OR'ed if supported */ static unsigned long part10_detect_pace_capabilities(sc_reader_t *reader, SCARDHANDLE card_handle) { u8 pace_capabilities_buf[] = { PACE_FUNCTION_GetReaderPACECapabilities,/* idxFunction */ 0, 0, /* lengthInputData */ }; u8 rbuf[7]; u8 *p = rbuf; DWORD rcount = sizeof rbuf; struct pcsc_private_data *priv; unsigned long flags = 0; if (!reader) goto err; priv = reader->drv_data; if (!priv) goto err; 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), &rcount)) { sc_log(reader->ctx, "PC/SC v2 part 10 amd1: Get PACE properties failed!"); goto err; } if (rcount != 7) goto err; /* Result */ if ((uint32_t) *p != 0) goto err; p += sizeof(uint32_t); /* length_OutputData */ if ((uint16_t) *p != 1) goto err; p += sizeof(uint16_t); if (*p & PACE_CAPABILITY_eSign) flags |= SC_READER_CAP_PACE_ESIGN; if (*p & PACE_CAPABILITY_eID) flags |= SC_READER_CAP_PACE_EID; if (*p & PACE_CAPABILITY_generic) flags |= SC_READER_CAP_PACE_GENERIC; if (*p & PACE_CAPABILITY_DestroyPACEChannel) flags |= SC_READER_CAP_PACE_DESTROY_CHANNEL; } err: return flags; } static int part10_find_property_by_tag(unsigned char buffer[], DWORD length, int tag_searched); /** * @brief Detects reader's maximum data size * * @param reader reader to probe (\c get_tlv_properties must be initialized) * * @return maximum data size */ static size_t part10_detect_max_data(sc_reader_t *reader, SCARDHANDLE card_handle) { u8 rbuf[256]; DWORD rcount = sizeof rbuf; struct pcsc_private_data *priv = NULL; /* 0 means extended APDU not supported */ size_t max_data = 0; int r; if (!reader) goto err; priv = reader->drv_data; if (!priv) goto err; 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!"); goto err; } r = part10_find_property_by_tag(rbuf, rcount, PCSCv2_PART10_PROPERTY_dwMaxAPDUDataSize); sc_log(reader->ctx, "get dwMaxAPDUDataSize property returned %i", r); /* 256 < X <= 0x10000: short and extended APDU of up to X bytes of data */ if (r > 0x100 && r <= 0x10000) max_data = r; } err: return max_data; } static int part10_get_vendor_product(struct sc_reader *reader, SCARDHANDLE card_handle, int *id_vendor, int *id_product) { u8 rbuf[256]; DWORD rcount = sizeof rbuf; struct pcsc_private_data *priv; int this_vendor = -1, this_product = -1; if (!reader) return SC_ERROR_INVALID_ARGUMENTS; priv = reader->drv_data; if (!priv) return SC_ERROR_INVALID_ARGUMENTS; 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!"); return SC_ERROR_TRANSMIT_FAILED; } this_vendor = part10_find_property_by_tag(rbuf, rcount, PCSCv2_PART10_PROPERTY_wIdVendor); this_product = part10_find_property_by_tag(rbuf, rcount, PCSCv2_PART10_PROPERTY_wIdProduct); } sc_log(reader->ctx, "id_vendor=%04x id_product=%04x", this_vendor, this_product); if (id_vendor) *id_vendor = this_vendor; if (id_product) *id_product = this_product; return SC_SUCCESS; } 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; 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) { 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 ((rcount % sizeof(PCSC_TLV_STRUCTURE)) != 0 || rcount > sizeof buf) { sc_log(ctx, "Inconsistent TLV from reader!"); return; } 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->tag); } } /* Set reader capabilities based on detected IOCTLs */ if (priv->verify_ioctl || (priv->verify_ioctl_start && priv->verify_ioctl_finish)) { const char *log_text = "Reader supports pinpad PIN verification"; if (priv->gpriv->enable_pinpad) { sc_log(ctx, "%s", log_text); reader->capabilities |= SC_READER_CAP_PIN_PAD; } else { sc_log(ctx, "%s %s", log_text, log_disabled); } } if (priv->modify_ioctl || (priv->modify_ioctl_start && priv->modify_ioctl_finish)) { const char *log_text = "Reader supports pinpad PIN modification"; if (priv->gpriv->enable_pinpad) { sc_log(ctx, "%s", log_text); reader->capabilities |= SC_READER_CAP_PIN_PAD; } else { sc_log(ctx, "%s %s", log_text, log_disabled); } } /* 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 && 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 *) &buf; if (caps->wLcdLayout > 0) { sc_log(ctx, "Reader has a display: %04X", caps->wLcdLayout); reader->capabilities |= SC_READER_CAP_DISPLAY; } else sc_log(ctx, "Reader does not have a display."); } #endif if (rcount == sizeof(PIN_PROPERTIES_STRUCTURE)) { 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 { sc_log(ctx, "Reader does not have a display."); } } else { sc_log(ctx, "Returned PIN properties structure has bad length (%lu/%"SC_FORMAT_LEN_SIZE_T"u)", (unsigned long)rcount, sizeof(PIN_PROPERTIES_STRUCTURE)); } } } if (priv->pace_ioctl) { const char *log_text = "Reader supports PACE"; if (priv->gpriv->enable_pace) { reader->capabilities |= part10_detect_pace_capabilities(reader, card_handle); if (reader->capabilities & SC_READER_CAP_PACE_GENERIC) sc_log(ctx, "%s", log_text); } else { sc_log(ctx, "%s %s", log_text, log_disabled); } } 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 */ 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 (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, buf, &rcount) == SCARD_S_SUCCESS && rcount > 0) { /* add NUL termination, just in case... */ buf[(sizeof buf)-1] = '\0'; reader->vendor = strdup((char *) buf); } 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; } } } int pcsc_add_reader(sc_context_t *ctx, char *reader_name, size_t reader_name_len, sc_reader_t **out_reader) { int ret = SC_ERROR_INTERNAL; struct pcsc_global_private_data *gpriv = (struct pcsc_global_private_data *) ctx->reader_drv_data; struct pcsc_private_data *priv; sc_reader_t *reader; sc_log(ctx, "Adding new PC/SC reader '%s'", reader_name); if ((reader = calloc(1, sizeof(sc_reader_t))) == NULL) { ret = SC_ERROR_OUT_OF_MEMORY; goto err1; } *out_reader = reader; if ((priv = calloc(1, sizeof(struct pcsc_private_data))) == NULL) { ret = SC_ERROR_OUT_OF_MEMORY; goto err1; } priv->gpriv = gpriv; reader->drv_data = priv; reader->ops = &pcsc_ops; reader->driver = &pcsc_drv; if ((reader->name = strdup(reader_name)) == NULL) { ret = SC_ERROR_OUT_OF_MEMORY; 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); if (ret == SC_SUCCESS) { refresh_attributes(reader); } err1: return ret; } static int pcsc_detect_readers(sc_context_t *ctx) { struct pcsc_global_private_data *gpriv = (struct pcsc_global_private_data *) ctx->reader_drv_data; DWORD active_proto, reader_buf_size = 0; SCARDHANDLE card_handle; LONG rv; char *reader_buf = NULL, *reader_name; const char *mszGroups = NULL; int ret = SC_ERROR_INTERNAL; unsigned int i; LOG_FUNC_CALLED(ctx); if (!gpriv) { /* FIXME: this is not the correct error */ ret = SC_ERROR_NO_READERS_FOUND; goto out; } if (gpriv->cardmod) { ret = SC_ERROR_NOT_ALLOWED; goto out; } sc_log(ctx, "Probing PC/SC readers"); gpriv->attached_reader = NULL; gpriv->removed_reader = NULL; do { if (gpriv->pcsc_ctx == (SCARDCONTEXT)-1) { /* * Cannot call SCardListReaders with -1 * context as in Windows ERROR_INVALID_HANDLE * is returned instead of SCARD_E_INVALID_HANDLE */ rv = SCARD_E_INVALID_HANDLE; } else { rv = gpriv->SCardListReaders(gpriv->pcsc_ctx, NULL, NULL, (LPDWORD) &reader_buf_size); /* * 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 = -1; gpriv->pcsc_wait_ctx = -1; /* reconnecting below may may restart PC/SC service */ rv = SCARD_E_INVALID_HANDLE; } } if (rv != SCARD_S_SUCCESS) { if (rv != (LONG)SCARD_E_INVALID_HANDLE) { PCSC_LOG(ctx, "SCardListReaders failed", rv); ret = pcsc_to_opensc_error(rv); goto out; } sc_log(ctx, "Establish PC/SC context"); 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; } /* try to fetch the list of readers again */ rv = SCARD_E_INVALID_HANDLE; } } while (rv != SCARD_S_SUCCESS); /* 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; } rv = gpriv->SCardListReaders(gpriv->pcsc_ctx, mszGroups, reader_buf, (LPDWORD) &reader_buf_size); if (rv != SCARD_S_SUCCESS) { PCSC_LOG(ctx, "SCardListReaders failed", rv); ret = pcsc_to_opensc_error(rv); goto out; } /* 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) { 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; } } 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; if (priv->reader_state.dwEventState & SCARD_STATE_EXCLUSIVE) continue; rv = SCARD_E_SHARING_VIOLATION; /* Use DIRECT mode only if there is no card in the reader */ if (!(reader->flags & SC_READER_CARD_PRESENT)) { #ifndef _WIN32 /* Apple 10.5.7 and pcsc-lite previous to v1.5.5 do not support 0 as protocol identifier */ rv = gpriv->SCardConnect(gpriv->pcsc_ctx, reader->name, SCARD_SHARE_DIRECT, SCARD_PROTOCOL_T0|SCARD_PROTOCOL_T1, &card_handle, &active_proto); #else rv = gpriv->SCardConnect(gpriv->pcsc_ctx, reader->name, SCARD_SHARE_DIRECT, 0, &card_handle, &active_proto); #endif PCSC_TRACE(reader, "SCardConnect(DIRECT)", rv); } if (rv == (LONG)SCARD_E_SHARING_VIOLATION) { /* Assume that there is a card in the reader in shared mode if * direct communication failed */ rv = gpriv->SCardConnect(gpriv->pcsc_ctx, reader->name, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0|SCARD_PROTOCOL_T1, &card_handle, &active_proto); PCSC_TRACE(reader, "SCardConnect(SHARED)", rv); reader->active_protocol = pcsc_proto_to_opensc(active_proto); } if (rv == SCARD_S_SUCCESS) { detect_reader_features(reader, card_handle); gpriv->SCardDisconnect(card_handle, SCARD_LEAVE_CARD); } } ret = SC_SUCCESS; out: free(reader_buf); LOG_FUNC_RETURN(ctx, ret); } /* Wait for an event to occur. */ 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; unsigned int num_watch, count, i; int r = SC_ERROR_INTERNAL, detect_readers = 0, detected_hotplug = 0; DWORD dwtimeout; LOG_FUNC_CALLED(ctx); if (!event_reader && !event && reader_states) { sc_log(ctx, "free allocated reader states"); free(*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) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); /* Find out the current status */ num_watch = 0; count = sc_ctx_get_reader_count(ctx); 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; rgReaderStates[num_watch].szReader = reader->name; if (priv->reader_state.szReader == NULL) { rgReaderStates[num_watch].dwCurrentState = SCARD_STATE_UNAWARE; } else { rgReaderStates[num_watch].dwCurrentState = priv->reader_state.dwEventState; } rgReaderStates[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 rgReaderStates[num_watch].szReader = "\\\\?PnP?\\Notification"; rgReaderStates[num_watch].dwCurrentState = SCARD_STATE_UNAWARE; rgReaderStates[num_watch].dwEventState = SCARD_STATE_UNAWARE; num_watch++; sc_log(ctx, "Trying to detect new readers"); #endif } } else { rgReaderStates = (SCARD_READERSTATE *)(*reader_states); for (num_watch = 0; rgReaderStates[num_watch].szReader; num_watch++) sc_log(ctx, "reuse 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 == (SCARDCONTEXT)-1) { rv = gpriv->SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &gpriv->pcsc_wait_ctx); if (rv != SCARD_S_SUCCESS) { gpriv->pcsc_wait_ctx = -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) { r = SC_ERROR_INTERNAL; goto out; } *event_reader = NULL; *event = 0; if (num_watch == 0) { 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); if (rv != SCARD_S_SUCCESS) { if (rv != (LONG)SCARD_E_TIMEOUT) { PCSC_LOG(ctx, "SCardGetStatusChange(1) failed", rv); r = pcsc_to_opensc_error(rv); goto out; } } /* Wait for a status change */ for( ; ; ) { SCARD_READERSTATE *rsp; sc_log(ctx, "Looping..."); /* Scan the current state of all readers to see if they * match any of the events we're polling for */ for (i = 0, rsp = rgReaderStates; i < num_watch; i++, rsp++) { DWORD state, prev_state; sc_log(ctx, "'%s' before=0x%08X now=0x%08X", rsp->szReader, (unsigned int)rsp->dwCurrentState, (unsigned int)rsp->dwEventState); prev_state = rsp->dwCurrentState; state = rsp->dwEventState; rsp->dwCurrentState = rsp->dwEventState; if (state & SCARD_STATE_CHANGED) { /* check for hotplug events */ if (!strcmp(rsp->szReader, "\\\\?PnP?\\Notification")) { sc_log(ctx, "detected hotplug event"); /* 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; /* 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; } 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; } } } } /* if a reader was detected, we need to create a new list of readers */ if (detected_hotplug) goto out; /* Set the timeout if caller wants to time out */ if (timeout == -1) { dwtimeout = INFINITE; } else dwtimeout = timeout; rv = gpriv->SCardGetStatusChange(gpriv->pcsc_wait_ctx, dwtimeout, rgReaderStates, num_watch); if (rv == (LONG)SCARD_E_CANCELLED) { /* C_Finalize was called, events don't matter */ r = SC_ERROR_EVENT_TIMEOUT; goto out; } if (rv == (LONG)SCARD_E_TIMEOUT) { r = SC_ERROR_EVENT_TIMEOUT; goto out; } if (rv != SCARD_S_SUCCESS) { PCSC_LOG(ctx, "SCardGetStatusChange(2) failed", rv); r = pcsc_to_opensc_error(rv); goto out; } } out: /* 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; } } if (detect_readers) { free(rgReaderStates); if (reader_states && *reader_states) *reader_states = NULL; } else { if (!reader_states) { free(rgReaderStates); } else if (*reader_states == NULL) { sc_log(ctx, "return allocated reader states"); *reader_states = rgReaderStates; } } LOG_FUNC_RETURN(ctx, r); } /* * Pinpad support, based on PC/SC v2 Part 10 interface * Similar to CCID in spirit. */ /* Local definitions */ #define SC_CCID_PIN_TIMEOUT 30 /* CCID definitions */ #define SC_CCID_PIN_ENCODING_BIN 0x00 #define SC_CCID_PIN_ENCODING_BCD 0x01 #define SC_CCID_PIN_ENCODING_ASCII 0x02 #define SC_CCID_PIN_UNITS_BYTES 0x80 /* Build a PIN verification block + APDU */ static int part10_build_verify_pin_block(struct sc_reader *reader, u8 * buf, size_t * size, struct sc_pin_cmd_data *data) { int offset = 0, count = 0; sc_apdu_t *apdu = data->apdu; u8 tmp; 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; pin_verify->bTimerOut2 = SC_CCID_PIN_TIMEOUT; /* bmFormatString */ tmp = 0x00; if (data->pin1.encoding == SC_PIN_ENCODING_ASCII) { tmp |= SC_CCID_PIN_ENCODING_ASCII; /* 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 |= off << 3; } } else if (data->pin1.encoding == SC_PIN_ENCODING_BCD) { tmp |= SC_CCID_PIN_ENCODING_BCD; tmp |= SC_CCID_PIN_UNITS_BYTES; } else if (data->pin1.encoding == SC_PIN_ENCODING_GLP) { /* see comment about GLP PINs in sec.c */ tmp |= SC_CCID_PIN_ENCODING_BCD; tmp |= 0x08 << 3; } else return SC_ERROR_NOT_SUPPORTED; pin_verify->bmFormatString = tmp; /* bmPINBlockString */ tmp = 0x00; if (data->pin1.encoding == SC_PIN_ENCODING_GLP) { /* 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) { /* * 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; /* bmPINLengthFormat */ tmp = 0x00; if (data->pin1.encoding == SC_PIN_ENCODING_GLP) { /* GLP PINs expect the effective PIN length from bit 4 */ tmp |= 0x04; } pin_verify->bmPINLengthFormat = tmp; /* bmPINLengthFormat */ if (!data->pin1.min_length || !data->pin1.max_length) return SC_ERROR_INVALID_ARGUMENTS; 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 */ if (reader->capabilities & SC_READER_CAP_DISPLAY) pin_verify->bNumberMessage = 0xFF; /* Default message */ else pin_verify->bNumberMessage = 0x00; /* No messages */ /* Ignore language and T=1 parameters. */ pin_verify->wLangId = HOST_TO_CCID_16(0x0000); pin_verify->bMsgIndex = 0x00; pin_verify->bTeoPrologue[0] = 0x00; pin_verify->bTeoPrologue[1] = 0x00; pin_verify->bTeoPrologue[2] = 0x00; /* APDU itself */ 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 */ count = sizeof(PIN_VERIFY_STRUCTURE) + offset; *size = count; return SC_SUCCESS; } /* Build a PIN modification block + APDU */ static int part10_build_modify_pin_block(struct sc_reader *reader, u8 * buf, size_t * size, struct sc_pin_cmd_data *data) { int offset = 0, count = 0; sc_apdu_t *apdu = data->apdu; u8 tmp; 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; /* PIN verification control message */ pin_modify->bTimerOut = SC_CCID_PIN_TIMEOUT; /* bTimeOut */ pin_modify->bTimerOut2 = SC_CCID_PIN_TIMEOUT; /* bTimeOut2 */ /* bmFormatString */ tmp = 0x00; if (pin_ref->encoding == SC_PIN_ENCODING_ASCII) { tmp |= SC_CCID_PIN_ENCODING_ASCII; } else if (pin_ref->encoding == SC_PIN_ENCODING_BCD) { tmp |= SC_CCID_PIN_ENCODING_BCD; tmp |= SC_CCID_PIN_UNITS_BYTES; } else if (pin_ref->encoding == SC_PIN_ENCODING_GLP) { /* see comment about GLP PINs in sec.c */ tmp |= SC_CCID_PIN_ENCODING_BCD; tmp |= 0x08 << 3; } else return SC_ERROR_NOT_SUPPORTED; pin_modify->bmFormatString = tmp; /* bmFormatString */ /* bmPINBlockString */ tmp = 0x00; if (pin_ref->encoding == SC_PIN_ENCODING_GLP) { /* 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) { if (pin_ref->pad_length <= 15) tmp |= pin_ref->pad_length; } pin_modify->bmPINBlockString = tmp; /* bmPINBlockString */ /* bmPINLengthFormat */ tmp = 0x00; if (pin_ref->encoding == SC_PIN_ENCODING_GLP) { /* GLP PINs expect the effective PIN length from bit 4 */ tmp |= 0x04; } pin_modify->bmPINLengthFormat = tmp; /* bmPINLengthFormat */ /* 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 = (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 * 0x01: New Pin, Confirm Pin * 0x03: Enter Old Pin, New Pin, Confirm Pin */ pin_modify->bConfirmPIN = data->flags & SC_PIN_CMD_IMPLICIT_CHANGE ? 0x01 : 0x03; pin_modify->bEntryValidationCondition = 0x02; /* bEntryValidationCondition, keypress only */ /* bNumberMessage flags * 0x02: Messages seen on Pinpad display: New Pin, Confirm Pin * 0x03: Messages seen on Pinpad display: Enter Old Pin, New Pin, Confirm Pin * Could be 0xFF too. */ if (reader->capabilities & SC_READER_CAP_DISPLAY) pin_modify->bNumberMessage = data->flags & SC_PIN_CMD_IMPLICIT_CHANGE ? 0x02 : 0x03; else pin_modify->bNumberMessage = 0x00; /* No messages */ /* Ignore language and T=1 parameters. */ pin_modify->wLangId = HOST_TO_CCID_16(0x0000); pin_modify->bMsgIndex1 = (data->flags & SC_PIN_CMD_IMPLICIT_CHANGE ? 0x01: 0x00); /* Default message indexes */ pin_modify->bMsgIndex2 = (data->flags & SC_PIN_CMD_IMPLICIT_CHANGE ? 0x02: 0x01); pin_modify->bMsgIndex3 = 0x02; pin_modify->bTeoPrologue[0] = 0x00; pin_modify->bTeoPrologue[1] = 0x00; pin_modify->bTeoPrologue[2] = 0x00; /* APDU itself */ 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 */ count = sizeof(PIN_MODIFY_STRUCTURE) + offset; *size = count; return SC_SUCCESS; } /* Find a given PCSC v2 part 10 property */ static int 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 < (long)length) { if (*p++ == tag_searched) { found = 1; break; } /* go to next tag */ len = *p++; p += len; } if (found) { len = *p++; switch(len) { case 1: value = *p; break; case 2: value = *p + (*(p+1)<<8); break; case 4: value = *p + (*(p+1)<<8) + (*(p+2)<<16) + (*(p+3)<<24); 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 */ static int part10_check_pin_min_max(sc_reader_t *reader, struct sc_pin_cmd_data *data) { int r; 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->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; r = pcsc_internal_transmit(reader, NULL, 0, buffer, &length, priv->get_tlv_properties); LOG_TEST_RET(reader->ctx, r, "PC/SC v2 part 10: Get TLV properties failed!"); /* minimum pin size */ r = part10_find_property_by_tag(buffer, length, PCSCv2_PART10_PROPERTY_bMinPINSize); if (r >= 0) { unsigned int value = r; if (pin_ref->min_length < value) pin_ref->min_length = r; } /* maximum pin size */ r = part10_find_property_by_tag(buffer, length, PCSCv2_PART10_PROPERTY_bMaxPINSize); if (r > 0) { unsigned int value = r; if (!pin_ref->max_length || pin_ref->max_length > value) pin_ref->max_length = r; } return 0; } /* Do the PIN command */ static int pcsc_pin_cmd(sc_reader_t *reader, struct sc_pin_cmd_data *data) { struct pcsc_private_data *priv = reader->drv_data; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; /* sbuf holds a pin verification/modification structure plus an APDU. */ u8 sbuf[sizeof(PIN_VERIFY_STRUCTURE)>sizeof(PIN_MODIFY_STRUCTURE)? sizeof(PIN_VERIFY_STRUCTURE)+SC_MAX_APDU_BUFFER_SIZE: sizeof(PIN_MODIFY_STRUCTURE)+SC_MAX_APDU_BUFFER_SIZE]; size_t rcount = sizeof(rbuf), scount = 0; int r; DWORD ioctl = 0; sc_apdu_t *apdu; LOG_FUNC_CALLED(reader->ctx); if (reader->ctx->flags & SC_CTX_FLAG_TERMINATE) return SC_ERROR_NOT_ALLOWED; if (priv->gpriv->SCardControl == NULL) return SC_ERROR_NOT_SUPPORTED; /* The APDU must be provided by the card driver */ if (!data->apdu) { sc_log(reader->ctx, "No APDU provided for PC/SC v2 pinpad verification!"); return SC_ERROR_NOT_SUPPORTED; } apdu = data->apdu; switch (data->cmd) { case SC_PIN_CMD_VERIFY: if (!(priv->verify_ioctl || (priv->verify_ioctl_start && priv->verify_ioctl_finish))) { sc_log(reader->ctx, "Pinpad reader does not support verification!"); return SC_ERROR_NOT_SUPPORTED; } part10_check_pin_min_max(reader, data); r = part10_build_verify_pin_block(reader, sbuf, &scount, data); ioctl = priv->verify_ioctl ? priv->verify_ioctl : priv->verify_ioctl_start; break; case SC_PIN_CMD_CHANGE: case SC_PIN_CMD_UNBLOCK: if (!(priv->modify_ioctl || (priv->modify_ioctl_start && priv->modify_ioctl_finish))) { sc_log(reader->ctx, "Pinpad reader does not support modification!"); return SC_ERROR_NOT_SUPPORTED; } part10_check_pin_min_max(reader, data); r = part10_build_modify_pin_block(reader, sbuf, &scount, data); ioctl = priv->modify_ioctl ? priv->modify_ioctl : priv->modify_ioctl_start; break; default: sc_log(reader->ctx, "Unknown PIN command %d", data->cmd); return SC_ERROR_NOT_SUPPORTED; } /* 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_log_hex(reader->ctx, "PC/SC v2 pinpad block", sbuf, scount); r = pcsc_internal_transmit(reader, sbuf, scount, rbuf, &rcount, ioctl); LOG_TEST_RET(reader->ctx, r, "PC/SC v2 pinpad: block transmit failed!"); /* finish the call if it was a two-phase operation */ if ((ioctl == priv->verify_ioctl_start) || (ioctl == priv->modify_ioctl_start)) { if (rcount != 0) { LOG_FUNC_RETURN(reader->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); } ioctl = (ioctl == priv->verify_ioctl_start) ? priv->verify_ioctl_finish : priv->modify_ioctl_finish; rcount = sizeof(rbuf); r = pcsc_internal_transmit(reader, sbuf, 0, rbuf, &rcount, ioctl); LOG_TEST_RET(reader->ctx, r, "PC/SC v2 pinpad: finish operation failed!"); } /* We expect only two bytes of result data (SW1 and SW2) */ if (rcount != 2) { LOG_FUNC_RETURN(reader->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); } /* Extract the SWs for the result APDU */ apdu->sw1 = (unsigned int) rbuf[rcount - 2]; apdu->sw2 = (unsigned int) rbuf[rcount - 1]; r = SC_SUCCESS; switch (((unsigned int) apdu->sw1 << 8) | apdu->sw2) { 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; } LOG_TEST_RET(reader->ctx, r, "PIN command failed"); /* PIN command completed, all is good */ return SC_SUCCESS; } static int transform_pace_input(struct establish_pace_channel_input *pace_input, u8 *sbuf, size_t *scount) { u8 *p = sbuf; uint16_t lengthInputData, lengthCertificateDescription; uint8_t lengthCHAT, lengthPIN; if (!pace_input || !sbuf || !scount) return SC_ERROR_INVALID_ARGUMENTS; lengthInputData = 5 + pace_input->pin_length + pace_input->chat_length + pace_input->certificate_description_length; if ((unsigned)(lengthInputData + 3) > *scount) return SC_ERROR_OUT_OF_MEMORY; /* idxFunction */ *(p++) = PACE_FUNCTION_EstablishPACEChannel; /* lengthInputData */ memcpy(p, &lengthInputData, sizeof lengthInputData); p += sizeof lengthInputData; *(p++) = pace_input->pin_id; /* length CHAT */ lengthCHAT = pace_input->chat_length; *(p++) = lengthCHAT; /* CHAT */ memcpy(p, pace_input->chat, lengthCHAT); p += lengthCHAT; /* length PIN */ lengthPIN = pace_input->pin_length; *(p++) = lengthPIN; /* PIN */ memcpy(p, pace_input->pin, lengthPIN); p += lengthPIN; /* lengthCertificateDescription */ lengthCertificateDescription = pace_input->certificate_description_length; memcpy(p, &lengthCertificateDescription, sizeof lengthCertificateDescription); p += sizeof lengthCertificateDescription; /* certificate description */ memcpy(p, pace_input->certificate_description, lengthCertificateDescription); *scount = lengthInputData + 3; return SC_SUCCESS; } static int transform_pace_output(u8 *rbuf, size_t rbuflen, struct establish_pace_channel_output *pace_output) { size_t parsed = 0; uint8_t ui8; uint16_t ui16; if (!rbuf || !pace_output) return SC_ERROR_INVALID_ARGUMENTS; /* Result */ if (parsed+4 > rbuflen) return SC_ERROR_UNKNOWN_DATA_RECEIVED; memcpy(&pace_output->result, &rbuf[parsed], 4); parsed += 4; /* length_OutputData */ if (parsed+2 > rbuflen) return SC_ERROR_UNKNOWN_DATA_RECEIVED; memcpy(&ui16, &rbuf[parsed], 2); if ((size_t)ui16+6 != rbuflen) return SC_ERROR_UNKNOWN_DATA_RECEIVED; parsed += 2; /* MSE:Set AT Statusbytes */ if (parsed+2 > rbuflen) return SC_ERROR_UNKNOWN_DATA_RECEIVED; pace_output->mse_set_at_sw1 = rbuf[parsed+0]; pace_output->mse_set_at_sw2 = rbuf[parsed+1]; parsed += 2; /* length_CardAccess */ if (parsed+2 > rbuflen) return SC_ERROR_UNKNOWN_DATA_RECEIVED; memcpy(&ui16, &rbuf[parsed], 2); /* do not just yet copy ui16 to pace_output->ef_cardaccess_length */ parsed += 2; /* EF_CardAccess */ if (parsed+ui16 > rbuflen) return SC_ERROR_UNKNOWN_DATA_RECEIVED; if (pace_output->ef_cardaccess) { /* caller wants EF.CardAccess */ if (pace_output->ef_cardaccess_length < ui16) return SC_ERROR_OUT_OF_MEMORY; /* now save ui16 to pace_output->ef_cardaccess_length */ pace_output->ef_cardaccess_length = ui16; memcpy(pace_output->ef_cardaccess, &rbuf[parsed], ui16); } else { /* caller does not want EF.CardAccess */ pace_output->ef_cardaccess_length = 0; } parsed += ui16; if (parsed < rbuflen) { /* The following elements are only present if the execution of PACE is * to be followed by an execution of Terminal Authentication Version 2 * as defined in [TR-03110]. These data are needed to perform the * Terminal Authentication. */ /* length_CARcurr */ ui8 = rbuf[parsed]; /* do not just yet copy ui8 to pace_output->recent_car_length */ parsed += 1; /* CARcurr */ if (parsed+ui8 > rbuflen) return SC_ERROR_UNKNOWN_DATA_RECEIVED; if (pace_output->recent_car) { /* caller wants most recent certificate authority reference */ if (pace_output->recent_car_length < ui8) return SC_ERROR_OUT_OF_MEMORY; /* now save ui8 to pace_output->recent_car_length */ pace_output->recent_car_length = ui8; memcpy(pace_output->recent_car, &rbuf[parsed], ui8); } else { /* caller does not want most recent certificate authority reference */ pace_output->recent_car_length = 0; } parsed += ui8; /* length_CARprev */ ui8 = rbuf[parsed]; /* do not just yet copy ui8 to pace_output->previous_car_length */ parsed += 1; /* length_CCARprev */ if (parsed+ui8 > rbuflen) return SC_ERROR_UNKNOWN_DATA_RECEIVED; if (pace_output->previous_car) { /* caller wants previous certificate authority reference */ if (pace_output->previous_car_length < ui8) return SC_ERROR_OUT_OF_MEMORY; /* now save ui8 to pace_output->previous_car_length */ pace_output->previous_car_length = ui8; memcpy(pace_output->previous_car, &rbuf[parsed], ui8); } else { /* caller does not want previous certificate authority reference */ pace_output->previous_car_length = 0; } parsed += ui8; /* length_IDicc */ if (parsed+2 > rbuflen) return SC_ERROR_UNKNOWN_DATA_RECEIVED; memcpy(&ui16, &rbuf[parsed], 2); /* do not just yet copy ui16 to pace_output->id_icc_length */ parsed += 2; /* IDicc */ if (parsed+ui16 > rbuflen) return SC_ERROR_UNKNOWN_DATA_RECEIVED; if (pace_output->id_icc) { /* caller wants Ephemeral PACE public key of the IFD */ if (pace_output->id_icc_length < ui16) return SC_ERROR_OUT_OF_MEMORY; /* now save ui16 to pace_output->id_icc_length */ pace_output->id_icc_length = ui16; memcpy(pace_output->id_icc, &rbuf[parsed], ui16); } else { /* caller does not want Ephemeral PACE public key of the IFD */ pace_output->id_icc_length = 0; } parsed += ui16; if (parsed < rbuflen) return SC_ERROR_UNKNOWN_DATA_RECEIVED; } else { pace_output->recent_car_length = 0; pace_output->previous_car_length = 0; pace_output->id_icc_length = 0; } return SC_SUCCESS; } static int pcsc_perform_pace(struct sc_reader *reader, void *input_pace, void *output_pace) { struct establish_pace_channel_input *pace_input = (struct establish_pace_channel_input *) input_pace; struct establish_pace_channel_output *pace_output = (struct establish_pace_channel_output *) output_pace; struct pcsc_private_data *priv; u8 rbuf[SC_MAX_EXT_APDU_BUFFER_SIZE], sbuf[SC_MAX_EXT_APDU_BUFFER_SIZE]; size_t rcount = sizeof rbuf, scount = sizeof sbuf; if (!reader || !(reader->capabilities & SC_READER_CAP_PACE_GENERIC)) return SC_ERROR_INVALID_ARGUMENTS; priv = reader->drv_data; if (!priv) return SC_ERROR_INVALID_ARGUMENTS; LOG_TEST_RET(reader->ctx, transform_pace_input(pace_input, sbuf, &scount), "Creating EstabishPACEChannel input data"); LOG_TEST_RET(reader->ctx, pcsc_internal_transmit(reader, sbuf, scount, rbuf, &rcount, priv->pace_ioctl), "Executing EstabishPACEChannel"); LOG_TEST_RET(reader->ctx, transform_pace_output(rbuf, rcount, pace_output), "Parsing EstabishPACEChannel output data"); return SC_SUCCESS; } static void detect_protocol(sc_reader_t *reader, SCARDHANDLE card_handle) { DWORD readers_len = 0, state, prot, atr_len = SC_MAX_ATR_SIZE; unsigned char atr[SC_MAX_ATR_SIZE]; struct pcsc_private_data *priv = reader->drv_data; /* attempt to detect protocol in use T0/T1/RAW */ DWORD rv = priv->gpriv->SCardStatus(card_handle, NULL, &readers_len, &state, &prot, atr, &atr_len); if (rv != SCARD_S_SUCCESS) { prot = SCARD_PROTOCOL_T0; } 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; struct pcsc_global_private_data *gpriv = (struct pcsc_global_private_data *) ctx->reader_drv_data; char reader_name[128]; DWORD reader_name_size = sizeof(reader_name); int ret = SC_ERROR_INTERNAL; LOG_FUNC_CALLED(ctx); if (!gpriv) { ret = SC_ERROR_NO_READERS_FOUND; goto out; } 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_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; if(SCARD_S_SUCCESS == gpriv->SCardGetAttrib(card_handle, SCARD_ATTR_DEVICE_SYSTEM_NAME_A, (LPBYTE) reader_name, &reader_name_size)) { sc_reader_t *reader = NULL; ret = pcsc_add_reader(ctx, reader_name, reader_name_size, &reader); if (ret == SC_SUCCESS) { struct pcsc_private_data *priv = reader->drv_data; 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); } } out: LOG_FUNC_RETURN(ctx, ret); } struct sc_reader_driver * sc_get_pcsc_driver(void) { pcsc_ops.init = pcsc_init; pcsc_ops.finish = pcsc_finish; pcsc_ops.detect_readers = pcsc_detect_readers; pcsc_ops.transmit = pcsc_transmit; pcsc_ops.detect_card_presence = pcsc_detect_card_presence; pcsc_ops.lock = pcsc_lock; pcsc_ops.unlock = pcsc_unlock; pcsc_ops.release = pcsc_release; pcsc_ops.connect = pcsc_connect; pcsc_ops.disconnect = pcsc_disconnect; pcsc_ops.perform_verify = pcsc_pin_cmd; pcsc_ops.wait_for_event = pcsc_wait_for_event; pcsc_ops.cancel = pcsc_cancel; pcsc_ops.reset = pcsc_reset; pcsc_ops.use_reader = pcsc_use_reader; pcsc_ops.perform_pace = pcsc_perform_pace; return &pcsc_drv; } #endif /* ENABLE_PCSC */ OpenSC-0.26.1/src/libopensc/reader-tr03119.c000066400000000000000000000753511474147347300201540ustar00rootroot00000000000000/* * reader-escape.c: implementation related to escape commands with pseudo APDUs * * 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 * 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 "reader-tr03119.h" #include "ccid-types.h" #include "internal.h" #include "libopensc/asn1.h" #include "libopensc/log.h" #include "libopensc/opensc.h" #include "libopensc/pace.h" #include #include #if _WIN32 /* FIXME might not always work */ #define htole16(x) (x) #define htole32(x) (x) #elif __APPLE__ #include #define htole16(x) OSSwapHostToLittleInt16(x) #define htole32(x) OSSwapHostToLittleInt32(x) #else #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; /* BitMap */ *bitmap = EAC_BITMAP_PACE|EAC_BITMAP_EID|EAC_BITMAP_ESIGN; return SC_SUCCESS; } const u8 escape_cla = 0xff; const u8 escape_ins = 0x9a; 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", SC_ASN1_BOOLEAN, SC_ASN1_TAG_BOOLEAN, 0, NULL, NULL }, { NULL , 0 , 0 , 0 , NULL , NULL } }; struct sc_asn1_entry g_int_as_octet_string[] = { { "int as octet string", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, { NULL , 0 , 0 , 0 , NULL , NULL } }; struct sc_asn1_entry g_octet_string[] = { { "octet string", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_OCTET_STRING, SC_ASN1_ALLOC, NULL, NULL }, { NULL , 0 , 0 , 0 , NULL , NULL } }; struct sc_asn1_entry g_numeric_string_as_octet_string[] = { { "utf8string", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_NUMERICSTRING, SC_ASN1_ALLOC, NULL, NULL }, { NULL , 0 , 0 , 0 , NULL , NULL } }; 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 }, { "transmittedPassword", SC_ASN1_STRUCT, SC_ASN1_CTX|0x02|SC_ASN1_CONS, SC_ASN1_OPTIONAL|SC_ASN1_ALLOC, NULL, NULL }, { "cHAT", SC_ASN1_STRUCT, SC_ASN1_CTX|0x03|SC_ASN1_CONS, SC_ASN1_OPTIONAL|SC_ASN1_ALLOC, NULL, NULL }, { "certificateDescription", SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x04|SC_ASN1_CONS, SC_ASN1_OPTIONAL|SC_ASN1_ALLOC, NULL, NULL }, { "hashOID", /* use an OCTET STRING to avoid a conversion to struct sc_object_id */ SC_ASN1_STRUCT, SC_ASN1_CTX|0x05|SC_ASN1_CONS, SC_ASN1_OPTIONAL|SC_ASN1_ALLOC, NULL, NULL }, { NULL , 0 , 0 , 0 , NULL , NULL } }; const struct sc_asn1_entry g_EstablishPACEChannelOutput_data[] = { { "errorCode", SC_ASN1_STRUCT, SC_ASN1_CTX|0x01|SC_ASN1_CONS, 0, NULL, NULL }, { "statusMSESetAT", SC_ASN1_STRUCT, SC_ASN1_CTX|0x02|SC_ASN1_CONS, 0, NULL, NULL }, { "efCardAccess", SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x03|SC_ASN1_CONS, SC_ASN1_ALLOC, NULL, NULL }, { "idPICC", SC_ASN1_STRUCT, SC_ASN1_CTX|0x04|SC_ASN1_CONS, SC_ASN1_OPTIONAL|SC_ASN1_ALLOC, NULL, NULL }, { "curCAR", SC_ASN1_STRUCT, SC_ASN1_CTX|0x05|SC_ASN1_CONS, SC_ASN1_OPTIONAL|SC_ASN1_ALLOC, NULL, NULL }, { "prevCAR", SC_ASN1_STRUCT, SC_ASN1_CTX|0x06|SC_ASN1_CONS, SC_ASN1_OPTIONAL|SC_ASN1_ALLOC, NULL, NULL }, { NULL , 0 , 0 , 0 , NULL , NULL } }; 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 } }; int escape_pace_input_to_buf(sc_context_t *ctx, const struct establish_pace_channel_input *input, unsigned char **asn1, size_t *asn1_len) { size_t pin_id_len = sizeof input->pin_id; struct sc_asn1_entry EstablishPACEChannelInput_data[ sizeof g_EstablishPACEChannelInput_data/ sizeof *g_EstablishPACEChannelInput_data]; struct sc_asn1_entry EstablishPACEChannel[ sizeof g_EstablishPACEChannel/ sizeof *g_EstablishPACEChannel]; struct sc_asn1_entry passwordID[ sizeof g_int_as_octet_string/ sizeof *g_int_as_octet_string]; struct sc_asn1_entry transmittedPassword[ sizeof g_numeric_string_as_octet_string/ sizeof *g_numeric_string_as_octet_string]; struct sc_asn1_entry cHAT[ sizeof g_octet_string/ sizeof *g_octet_string]; sc_copy_asn1_entry(g_EstablishPACEChannel, EstablishPACEChannel); sc_format_asn1_entry(EstablishPACEChannel, EstablishPACEChannelInput_data, 0, 1); sc_copy_asn1_entry(g_EstablishPACEChannelInput_data, EstablishPACEChannelInput_data); sc_format_asn1_entry(EstablishPACEChannelInput_data+0, passwordID, 0, 1); sc_copy_asn1_entry(g_int_as_octet_string, passwordID); sc_format_asn1_entry(passwordID, (unsigned char *) &input->pin_id, &pin_id_len, 1); if (input->pin) { sc_format_asn1_entry(EstablishPACEChannelInput_data+1, transmittedPassword, 0, 1); sc_copy_asn1_entry(g_numeric_string_as_octet_string, transmittedPassword); sc_format_asn1_entry(transmittedPassword, (unsigned char *) input->pin, (size_t *) &input->pin_length, 1); } if (input->chat) { sc_format_asn1_entry(EstablishPACEChannelInput_data+2, cHAT, 0, 1); sc_copy_asn1_entry(g_octet_string, cHAT); sc_format_asn1_entry(cHAT, (unsigned char *) input->chat, (size_t *) &input->chat_length, 1); } if (input->certificate_description) { sc_format_asn1_entry(EstablishPACEChannelInput_data+3, (unsigned char *) input->certificate_description, (size_t *) &input->certificate_description_length, 1); } return sc_asn1_encode(ctx, EstablishPACEChannel, asn1, asn1_len); } int escape_buf_to_pace_input(sc_context_t *ctx, const unsigned char *asn1, size_t asn1_len, struct establish_pace_channel_input *input) { size_t pin_id_len = sizeof input->pin_id; struct sc_asn1_entry EstablishPACEChannelInput_data[ sizeof g_EstablishPACEChannelInput_data/ sizeof *g_EstablishPACEChannelInput_data]; struct sc_asn1_entry EstablishPACEChannel[ sizeof g_EstablishPACEChannel/ sizeof *g_EstablishPACEChannel]; struct sc_asn1_entry passwordID[ sizeof g_int_as_octet_string/ sizeof *g_int_as_octet_string]; struct sc_asn1_entry transmittedPassword[ sizeof g_numeric_string_as_octet_string/ sizeof *g_numeric_string_as_octet_string]; struct sc_asn1_entry cHAT[ sizeof g_octet_string/ sizeof *g_octet_string]; /* FIXME handle hashOID */ sc_copy_asn1_entry(g_EstablishPACEChannel, EstablishPACEChannel); sc_format_asn1_entry(EstablishPACEChannel, EstablishPACEChannelInput_data, 0, 0); sc_copy_asn1_entry(g_EstablishPACEChannelInput_data, EstablishPACEChannelInput_data); sc_format_asn1_entry(EstablishPACEChannelInput_data+0, passwordID, 0, 0); sc_copy_asn1_entry(g_int_as_octet_string, passwordID); sc_format_asn1_entry(passwordID, &input->pin_id, &pin_id_len, 0); if (input->pin) { sc_format_asn1_entry(EstablishPACEChannelInput_data+1, transmittedPassword, 0, 0); sc_copy_asn1_entry(g_numeric_string_as_octet_string, transmittedPassword); sc_format_asn1_entry(transmittedPassword, (unsigned char *) &input->pin, &input->pin_length, 0); } if (input->chat) { sc_format_asn1_entry(EstablishPACEChannelInput_data+2, cHAT, 0, 0); sc_copy_asn1_entry(g_octet_string, cHAT); sc_format_asn1_entry(cHAT, (unsigned char *) &input->chat, &input->chat_length, 0); } if (input->certificate_description) { sc_format_asn1_entry(EstablishPACEChannelInput_data+3, (unsigned char *) &input->certificate_description, &input->certificate_description_length, 0); } LOG_TEST_RET(ctx, sc_asn1_decode(ctx, EstablishPACEChannel, asn1, asn1_len, NULL, NULL), "Error decoding EstablishPACEChannel"); if (pin_id_len != sizeof input->pin_id) return SC_ERROR_UNKNOWN_DATA_RECEIVED; return SC_SUCCESS; } int escape_pace_output_to_buf(sc_context_t *ctx, const struct establish_pace_channel_output *output, unsigned char **asn1, size_t *asn1_len) { uint16_t status_mse_set_at = ((output->mse_set_at_sw1 & 0xff) << 8) | output->mse_set_at_sw2; size_t result_len = sizeof output->result, status_mse_set_at_len = sizeof status_mse_set_at; struct sc_asn1_entry EstablishPACEChannelOutput_data[ sizeof g_EstablishPACEChannelOutput_data/ sizeof *g_EstablishPACEChannelOutput_data]; struct sc_asn1_entry EstablishPACEChannel[ sizeof g_EstablishPACEChannel/ sizeof *g_EstablishPACEChannel]; struct sc_asn1_entry errorCode[ sizeof g_octet_string/ sizeof *g_octet_string]; struct sc_asn1_entry statusMSESetAT[ sizeof g_octet_string/ sizeof *g_octet_string]; struct sc_asn1_entry idPICC[ sizeof g_octet_string/ sizeof *g_octet_string]; struct sc_asn1_entry curCAR[ sizeof g_octet_string/ sizeof *g_octet_string]; struct sc_asn1_entry prevCAR[ sizeof g_octet_string/ sizeof *g_octet_string]; sc_copy_asn1_entry(g_EstablishPACEChannel, EstablishPACEChannel); sc_format_asn1_entry(EstablishPACEChannel, EstablishPACEChannelOutput_data, 0, 1); sc_copy_asn1_entry(g_EstablishPACEChannelOutput_data, EstablishPACEChannelOutput_data); sc_format_asn1_entry(EstablishPACEChannelOutput_data+0, errorCode, 0, 1); sc_copy_asn1_entry(g_octet_string, errorCode); sc_format_asn1_entry(errorCode, (unsigned char *) &output->result, &result_len, 1); sc_format_asn1_entry(EstablishPACEChannelOutput_data+1, statusMSESetAT, 0, 1); sc_copy_asn1_entry(g_octet_string, statusMSESetAT); sc_format_asn1_entry(statusMSESetAT, &status_mse_set_at, &status_mse_set_at_len, 1); if (output->ef_cardaccess) { sc_format_asn1_entry(EstablishPACEChannelOutput_data+2, output->ef_cardaccess, (size_t *) &output->ef_cardaccess_length, 1); } if (output->id_icc) { sc_format_asn1_entry(EstablishPACEChannelOutput_data+3, idPICC, 0, 1); sc_copy_asn1_entry(g_octet_string, idPICC); sc_format_asn1_entry(idPICC, output->id_icc, (size_t *) &output->id_icc_length, 1); } if (output->recent_car) { sc_format_asn1_entry(EstablishPACEChannelOutput_data+4, curCAR, 0, 1); sc_copy_asn1_entry(g_octet_string, curCAR); sc_format_asn1_entry(curCAR, output->recent_car, (size_t *) &output->recent_car_length, 1); } if (output->previous_car) { sc_format_asn1_entry(EstablishPACEChannelOutput_data+5, prevCAR, 0, 1); sc_copy_asn1_entry(g_octet_string, prevCAR); sc_format_asn1_entry(prevCAR, output->previous_car, (size_t *) &output->previous_car_length, 1); } return sc_asn1_encode(ctx, EstablishPACEChannel, asn1, asn1_len); } int escape_buf_to_pace_output(sc_context_t *ctx, const unsigned char *asn1, size_t asn1_len, struct establish_pace_channel_output *output) { uint16_t status_mse_set_at; size_t result_len = sizeof output->result, status_mse_set_at_len = sizeof status_mse_set_at; struct sc_asn1_entry EstablishPACEChannelOutput_data[ sizeof g_EstablishPACEChannelOutput_data/ sizeof *g_EstablishPACEChannelOutput_data]; struct sc_asn1_entry EstablishPACEChannel[ sizeof g_EstablishPACEChannel/ sizeof *g_EstablishPACEChannel]; struct sc_asn1_entry errorCode[ sizeof g_octet_string/ sizeof *g_octet_string]; struct sc_asn1_entry statusMSESetAT[ sizeof g_octet_string/ sizeof *g_octet_string]; struct sc_asn1_entry idPICC[ sizeof g_octet_string/ sizeof *g_octet_string]; struct sc_asn1_entry curCAR[ sizeof g_octet_string/ sizeof *g_octet_string]; struct sc_asn1_entry prevCAR[ sizeof g_octet_string/ sizeof *g_octet_string]; sc_copy_asn1_entry(g_EstablishPACEChannel, EstablishPACEChannel); sc_format_asn1_entry(EstablishPACEChannel, EstablishPACEChannelOutput_data, 0, 0); sc_copy_asn1_entry(g_EstablishPACEChannelOutput_data, EstablishPACEChannelOutput_data); sc_format_asn1_entry(EstablishPACEChannelOutput_data+0, errorCode, 0, 0); sc_format_asn1_entry(EstablishPACEChannelOutput_data+1, statusMSESetAT, 0, 0); sc_format_asn1_entry(EstablishPACEChannelOutput_data+2, &output->ef_cardaccess, &output->ef_cardaccess_length, 0); sc_format_asn1_entry(EstablishPACEChannelOutput_data+3, idPICC, 0, 0); sc_format_asn1_entry(EstablishPACEChannelOutput_data+4, curCAR, 0, 0); sc_format_asn1_entry(EstablishPACEChannelOutput_data+5, prevCAR, 0, 0); sc_copy_asn1_entry(g_octet_string, errorCode); sc_format_asn1_entry(errorCode, &output->result, &result_len, 0); /* we already allocated memory for the result */ errorCode->flags = 0; sc_copy_asn1_entry(g_octet_string, statusMSESetAT); sc_format_asn1_entry(statusMSESetAT, &status_mse_set_at, &status_mse_set_at_len, 0); /* we already allocated memory for the result */ statusMSESetAT->flags = 0; sc_copy_asn1_entry(g_octet_string, idPICC); sc_format_asn1_entry(idPICC, &output->id_icc, &output->id_icc_length, 0); sc_copy_asn1_entry(g_octet_string, curCAR); sc_format_asn1_entry(curCAR, &output->recent_car, &output->recent_car_length, 0); sc_copy_asn1_entry(g_octet_string, prevCAR); sc_format_asn1_entry(prevCAR, &output->previous_car, &output->previous_car_length, 0); LOG_TEST_RET(ctx, sc_asn1_decode(ctx, EstablishPACEChannel, asn1, asn1_len, NULL, NULL), "Error decoding EstablishPACEChannel"); if (status_mse_set_at_len != sizeof status_mse_set_at || result_len != sizeof output->result) return SC_ERROR_UNKNOWN_DATA_RECEIVED; output->mse_set_at_sw1 = (status_mse_set_at >> 8) & 0xff; output->mse_set_at_sw2 = status_mse_set_at & 0xff; return SC_SUCCESS; } #define CCID_PIN_TIMEOUT 30 #define CCID_DISPLAY_DEFAULT 0xff static int escape_pin_cmd_to_buf(sc_context_t *ctx, const struct sc_pin_cmd_data *data, unsigned char **pc_to_rdr_secure, size_t *pc_to_rdr_secure_len) { PC_to_RDR_Secure_t *secure; abPINDataStucture_Modification_t *modify; abPINDataStucture_Verification_t *verify; uint16_t wLangId = 0, bTeoPrologue2 = 0, wPINMaxExtraDigit; uint8_t bTimeOut = CCID_PIN_TIMEOUT, bNumberMessage = CCID_DISPLAY_DEFAULT, bTeoPrologue1 = 0, bMsgIndex = 0, bMessageType = 0x69, bSlot = 0, bSeq = 0, bBWI = 0xff, wLevelParameter = 0, bEntryValidationCondition = CCID_ENTRY_VALIDATE, bmFormatString, bmPINLengthFormat, bmPINBlockString; const struct sc_pin_cmd_pin *pin_ref; int r; unsigned char *pinapdu = NULL; size_t pinapdu_len = 0; if (!data || !pc_to_rdr_secure || !pc_to_rdr_secure_len) { r = SC_ERROR_INVALID_ARGUMENTS; goto err; } pin_ref = data->flags & SC_PIN_CMD_IMPLICIT_CHANGE ? &data->pin2 : &data->pin1; wPINMaxExtraDigit = htole16( (0xff & pin_ref->min_length) << 8) | (pin_ref->max_length & 0xff); bmFormatString = CCID_PIN_UNITS_BYTES | ((pin_ref->offset & 0xf) << 3); switch (pin_ref->encoding) { case SC_PIN_ENCODING_ASCII: bmFormatString |= CCID_PIN_ENCODING_ASCII; break; case SC_PIN_ENCODING_BCD: bmFormatString |= CCID_PIN_ENCODING_BCD; break; default: r = SC_ERROR_INVALID_ARGUMENTS; goto err; } /* GLP PINs expect the effective PIN length from bit 4 */ bmPINLengthFormat = pin_ref->encoding == SC_PIN_ENCODING_GLP ? 0x04 : 0x00; if (pin_ref->encoding == SC_PIN_ENCODING_GLP) { /* 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 = (uint8_t) pin_ref->pad_length; } else { bmPINBlockString = 0x00; } r = sc_apdu_get_octets(ctx, data->apdu, &pinapdu, &pinapdu_len, SC_PROTO_T1); if (r < 0) goto err; switch (data->cmd) { case SC_PIN_CMD_VERIFY: *pc_to_rdr_secure_len = sizeof *secure + 1 + sizeof *verify + pinapdu_len; break; case SC_PIN_CMD_CHANGE: *pc_to_rdr_secure_len = sizeof *secure + 1 + sizeof *modify + 3 + pinapdu_len; break; default: r = SC_ERROR_INVALID_ARGUMENTS; goto err; } *pc_to_rdr_secure = malloc(*pc_to_rdr_secure_len); if (!*pc_to_rdr_secure) { r = SC_ERROR_OUT_OF_MEMORY; goto err; } secure = (PC_to_RDR_Secure_t *) *pc_to_rdr_secure; secure->bMessageType = bMessageType; secure->dwLength = htole32((*pc_to_rdr_secure_len) - sizeof *secure); secure->bSlot = bSlot; secure->bSeq = bSeq; secure->bBWI = bBWI; secure->wLevelParameter = wLevelParameter; switch (data->cmd) { case SC_PIN_CMD_VERIFY: /* bPINOperation */ *((*pc_to_rdr_secure) + sizeof *secure) = CCID_OPERATION_VERIFY; verify = (abPINDataStucture_Verification_t *) ((*pc_to_rdr_secure) + sizeof *secure + 1); verify->bTimeOut = bTimeOut; verify->bmFormatString = bmFormatString; verify->bmPINBlockString = bmPINBlockString; verify->bmPINLengthFormat = bmPINLengthFormat; verify->wPINMaxExtraDigit = wPINMaxExtraDigit; verify->bEntryValidationCondition = bEntryValidationCondition; verify->bNumberMessage = bNumberMessage; verify->wLangId = wLangId; verify->bMsgIndex = bMsgIndex; verify->bTeoPrologue1 = bTeoPrologue1; verify->bTeoPrologue2 = bTeoPrologue2; memcpy((*pc_to_rdr_secure) + sizeof *secure + 1 + sizeof *verify, pinapdu, pinapdu_len); break; case SC_PIN_CMD_CHANGE: /* bPINOperation */ *((*pc_to_rdr_secure) + sizeof *secure) = CCID_OPERATION_MODIFY; modify = (abPINDataStucture_Modification_t *) ((*pc_to_rdr_secure) + sizeof *secure + 1); modify->bTimeOut = bTimeOut; modify->bmFormatString = bmFormatString; modify->bmPINBlockString = bmPINBlockString; modify->bmPINLengthFormat = bmPINLengthFormat; if (!(data->flags & SC_PIN_CMD_IMPLICIT_CHANGE) && data->pin1.offset) { modify->bInsertionOffsetOld = (uint8_t) data->pin1.offset - 5; } else { modify->bInsertionOffsetOld = 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); modify->bEntryValidationCondition = bEntryValidationCondition; modify->bNumberMessage = bNumberMessage; modify->wLangId = wLangId; modify->bMsgIndex1 = bMsgIndex; *((*pc_to_rdr_secure) + sizeof *secure + 1 + sizeof *modify + 0) = bTeoPrologue1; *((*pc_to_rdr_secure) + sizeof *secure + 1 + sizeof *modify + 1) = bTeoPrologue1; *((*pc_to_rdr_secure) + sizeof *secure + 1 + sizeof *modify + 2) = bTeoPrologue1; memcpy((*pc_to_rdr_secure) + sizeof *secure + 1 + sizeof *modify + 3, pinapdu, pinapdu_len); break; default: r = SC_ERROR_INVALID_ARGUMENTS; goto err; } r = SC_SUCCESS; err: free(pinapdu); if (r < 0 && pc_to_rdr_secure && *pc_to_rdr_secure) { free(*pc_to_rdr_secure); *pc_to_rdr_secure = NULL; } return r; } #define CCID_BSTATUS_OK_ACTIVE 0x00 /** No error. An ICC is present and active */ static int escape_buf_to_verify_result(sc_context_t *ctx, const unsigned char *rdr_to_pc_datablock, size_t rdr_to_pc_datablock_len, sc_apdu_t *apdu) { RDR_to_PC_DataBlock_t *datablock = (RDR_to_PC_DataBlock_t *) rdr_to_pc_datablock; if (!rdr_to_pc_datablock || rdr_to_pc_datablock_len < sizeof *datablock || datablock->bMessageType != 0x80) return SC_ERROR_UNKNOWN_DATA_RECEIVED; if (datablock->bStatus != CCID_BSTATUS_OK_ACTIVE) return SC_ERROR_TRANSMIT_FAILED; return sc_apdu_set_resp(ctx, apdu, rdr_to_pc_datablock + sizeof *datablock, htole32(datablock->dwLength)); } static int escape_perform_verify(struct sc_reader *reader, struct sc_pin_cmd_data *data) { u8 rbuf[0xff]; sc_apdu_t apdu; int r; memset(&apdu, 0, sizeof(apdu)); apdu.cse = SC_APDU_CASE_4_SHORT; apdu.cla = escape_cla; apdu.ins = escape_ins; apdu.p1 = escape_p1_PIN; apdu.p2 = escape_p2_PC_to_RDR_Secure; apdu.resp = rbuf; apdu.resplen = sizeof rbuf; apdu.le = sizeof rbuf; if (!reader || !reader->ops || !reader->ops->transmit) { r = SC_ERROR_NOT_SUPPORTED; goto err; } r = escape_pin_cmd_to_buf(reader->ctx, data, (unsigned char **) &apdu.data, &apdu.datalen); if (r < 0) { sc_log(reader->ctx, "Error encoding PC_to_RDR_Secure"); goto err; } apdu.lc = apdu.datalen; r = reader->ops->transmit(reader, &apdu); if (r < 0) { sc_log(reader->ctx, "Error performing PC_to_RDR_Secure"); goto err; } if (apdu.sw1 != 0x90 && apdu.sw2 != 0x00) { sc_log(reader->ctx, "Error decoding PC_to_RDR_Secure"); r = SC_ERROR_NOT_SUPPORTED; goto err; } r = escape_buf_to_verify_result(reader->ctx, apdu.resp, apdu.resplen, data->apdu); err: free((unsigned char *) apdu.data); return r; } static int escape_perform_pace(struct sc_reader *reader, void *establish_pace_channel_input, void *establish_pace_channel_output) { u8 rbuf[0xffff]; sc_apdu_t apdu; int r; struct establish_pace_channel_input *input = establish_pace_channel_input; struct establish_pace_channel_output *output = establish_pace_channel_output; memset(&apdu, 0, sizeof(apdu)); apdu.cse = SC_APDU_CASE_4_EXT; apdu.cla = escape_cla; apdu.ins = escape_ins; apdu.p1 = escape_p1_PIN; apdu.p2 = escape_p2_EstablishPACEChannel; apdu.resp = rbuf; apdu.resplen = sizeof rbuf; apdu.le = sizeof rbuf; if (!reader || !reader->ops || !reader->ops->transmit) { r = SC_ERROR_NOT_SUPPORTED; goto err; } r = escape_pace_input_to_buf(reader->ctx, input, (unsigned char **) &apdu.data, &apdu.datalen); if (r < 0) { sc_log(reader->ctx, "Error encoding EstablishPACEChannel"); goto err; } apdu.lc = apdu.datalen; r = reader->ops->transmit(reader, &apdu); if (r < 0) { sc_log(reader->ctx, "Error performing EstablishPACEChannel"); goto err; } if (apdu.sw1 != 0x90 && apdu.sw2 != 0x00) { sc_log(reader->ctx, "Error decoding EstablishPACEChannel"); r = SC_ERROR_NOT_SUPPORTED; goto err; } r = escape_buf_to_pace_output(reader->ctx, apdu.resp, apdu.resplen, output); err: free((unsigned char *) apdu.data); return r; } struct sc_asn1_entry g_PACECapabilities_data[] = { { "capabilityPACE", SC_ASN1_STRUCT, SC_ASN1_CTX|0x01|SC_ASN1_CONS, SC_ASN1_ALLOC, NULL, NULL }, { "capabilityEID", SC_ASN1_STRUCT, SC_ASN1_CTX|0x02|SC_ASN1_CONS, SC_ASN1_ALLOC, NULL, NULL }, { "capabilityESign", SC_ASN1_STRUCT, SC_ASN1_CTX|0x03|SC_ASN1_CONS, SC_ASN1_ALLOC, NULL, NULL }, { "capabilityDestroy", SC_ASN1_STRUCT, SC_ASN1_CTX|0x04|SC_ASN1_CONS, SC_ASN1_ALLOC, NULL, NULL }, { NULL , 0 , 0 , 0 , NULL , NULL } }; struct sc_asn1_entry g_PACECapabilities[] = { { "PACECapabilities", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE|SC_ASN1_CONS, 0, NULL, NULL }, { NULL , 0 , 0 , 0 , NULL , NULL } }; int escape_buf_to_pace_capabilities(sc_context_t *ctx, const unsigned char *asn1, size_t asn1_len, unsigned long *sc_reader_t_capabilities) { int pace = 0, eid = 0, esign = 0, destroy = 0; struct sc_asn1_entry PACECapabilities_data[ sizeof g_PACECapabilities_data/ sizeof *g_PACECapabilities_data]; struct sc_asn1_entry PACECapabilities[ sizeof g_PACECapabilities/ sizeof *g_PACECapabilities]; struct sc_asn1_entry capabilityPACE[ sizeof g_boolean/ sizeof *g_boolean]; struct sc_asn1_entry capabilityEID[ sizeof g_boolean/ sizeof *g_boolean]; struct sc_asn1_entry capabilityESign[ sizeof g_boolean/ sizeof *g_boolean]; struct sc_asn1_entry capabilityDestroy[ sizeof g_boolean/ sizeof *g_boolean]; sc_copy_asn1_entry(g_PACECapabilities, PACECapabilities); sc_format_asn1_entry(PACECapabilities, PACECapabilities_data, 0, 1); sc_copy_asn1_entry(g_PACECapabilities_data, PACECapabilities_data); sc_format_asn1_entry(PACECapabilities_data+0, &capabilityPACE, NULL, 1); sc_format_asn1_entry(PACECapabilities_data+1, &capabilityEID, NULL, 1); sc_format_asn1_entry(PACECapabilities_data+2, &capabilityESign, NULL, 1); sc_format_asn1_entry(PACECapabilities_data+3, &capabilityDestroy, NULL, 1); sc_copy_asn1_entry(g_boolean, capabilityPACE); sc_format_asn1_entry(capabilityPACE+0, &pace, NULL, 0); sc_copy_asn1_entry(g_boolean, capabilityEID); sc_format_asn1_entry(capabilityEID+0, &eid, NULL, 0); sc_copy_asn1_entry(g_boolean, capabilityESign); sc_format_asn1_entry(capabilityESign+0, &esign, NULL, 0); sc_copy_asn1_entry(g_boolean, capabilityDestroy); sc_format_asn1_entry(capabilityDestroy+0, &destroy, NULL, 0); LOG_TEST_RET(ctx, sc_asn1_decode(ctx, PACECapabilities, asn1, asn1_len, NULL, NULL), "Error decoding PACECapabilities"); /* We got a valid PACE Capabilities reply. There is currently no mechanism * to determine support PIN verification/modification with a escape * command. Since the reader implements this mechanism it is reasonable to * assume that PIN verification/modification is available. */ *sc_reader_t_capabilities = SC_READER_CAP_PIN_PAD; if (pace) *sc_reader_t_capabilities |= SC_READER_CAP_PACE_GENERIC; if (eid) *sc_reader_t_capabilities |= SC_READER_CAP_PACE_EID; if (esign) *sc_reader_t_capabilities |= SC_READER_CAP_PACE_ESIGN; if (destroy) *sc_reader_t_capabilities |= SC_READER_CAP_PACE_DESTROY_CHANNEL; return SC_SUCCESS; } int escape_pace_capabilities_to_buf(sc_context_t *ctx, const unsigned long sc_reader_t_capabilities, unsigned char **asn1, size_t *asn1_len) { int yes = 1, no = 0; struct sc_asn1_entry PACECapabilities_data[ sizeof g_PACECapabilities_data/ sizeof *g_PACECapabilities_data]; struct sc_asn1_entry PACECapabilities[ sizeof g_PACECapabilities/ sizeof *g_PACECapabilities]; struct sc_asn1_entry capabilityPACE[ sizeof g_boolean/ sizeof *g_boolean]; struct sc_asn1_entry capabilityEID[ sizeof g_boolean/ sizeof *g_boolean]; struct sc_asn1_entry capabilityESign[ sizeof g_boolean/ sizeof *g_boolean]; struct sc_asn1_entry capabilityDestroy[ sizeof g_boolean/ sizeof *g_boolean]; sc_copy_asn1_entry(g_EstablishPACEChannel, PACECapabilities); sc_format_asn1_entry(PACECapabilities, PACECapabilities_data, 0, 1); sc_copy_asn1_entry(g_PACECapabilities_data, PACECapabilities_data); sc_format_asn1_entry(PACECapabilities_data+0, &capabilityPACE, NULL, 1); sc_format_asn1_entry(PACECapabilities_data+1, &capabilityEID, NULL, 1); sc_format_asn1_entry(PACECapabilities_data+2, &capabilityESign, NULL, 1); sc_format_asn1_entry(PACECapabilities_data+3, &capabilityDestroy, NULL, 1); sc_copy_asn1_entry(g_boolean, capabilityPACE); sc_format_asn1_entry(capabilityPACE, sc_reader_t_capabilities & SC_READER_CAP_PACE_GENERIC ? &yes : &no, NULL, 1); sc_copy_asn1_entry(g_boolean, capabilityEID); sc_format_asn1_entry(capabilityEID, sc_reader_t_capabilities & SC_READER_CAP_PACE_EID ? &yes : &no, NULL, 1); sc_copy_asn1_entry(g_boolean, capabilityESign); sc_format_asn1_entry(capabilityESign, sc_reader_t_capabilities & SC_READER_CAP_PACE_ESIGN ? &yes : &no, NULL, 1); sc_copy_asn1_entry(g_boolean, capabilityDestroy); sc_format_asn1_entry(capabilityDestroy, sc_reader_t_capabilities & SC_READER_CAP_PACE_DESTROY_CHANNEL ? &yes : &no, NULL, 1); return sc_asn1_encode(ctx, PACECapabilities, asn1, asn1_len); } void sc_detect_escape_cmds(sc_reader_t *reader) { int error = 0; u8 rbuf[0xff+1]; sc_apdu_t apdu; unsigned long capabilities; if (reader && reader->ops && reader->ops->transmit) { memset(&apdu, 0, sizeof(apdu)); apdu.cse = SC_APDU_CASE_2_SHORT; apdu.cla = escape_cla; apdu.ins = escape_ins; apdu.p1 = escape_p1_PIN; apdu.p2 = escape_p2_GetReaderPACECapabilities; apdu.resp = rbuf; apdu.resplen = sizeof rbuf; apdu.le = sizeof rbuf; if (reader->ops->transmit(reader, &apdu) == SC_SUCCESS && apdu.sw1 == 0x90 && apdu.sw2 == 0x00 && escape_buf_to_pace_capabilities(reader->ctx, apdu.resp, apdu.resplen, &capabilities) == SC_SUCCESS) { if (capabilities & SC_READER_CAP_PIN_PAD && !(reader->capabilities & SC_READER_CAP_PIN_PAD)) { ((struct sc_reader_operations *) reader->ops)->perform_verify = escape_perform_verify; sc_log(reader->ctx, "Added escape command wrappers for PIN verification/modification to '%s'", reader->name); } if (capabilities & SC_READER_CAP_PACE_GENERIC && !(reader->capabilities & SC_READER_CAP_PACE_GENERIC)) { ((struct sc_reader_operations *) reader->ops)->perform_pace = escape_perform_pace; sc_log(reader->ctx, "Added escape command wrappers for PACE to '%s'", reader->name); } reader->capabilities |= capabilities; } else { error++; 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; if (reader->ops->transmit(reader, &apdu) == SC_SUCCESS && apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { if (!reader->vendor) { /* add NUL termination, just in case... */ rbuf[apdu.resplen] = '\0'; reader->vendor = strdup((const char *) rbuf); } } else { error++; } apdu.p1 = escape_p1_IFD; apdu.p2 = escape_p2_version_firmware; apdu.resplen = sizeof rbuf; if (reader->ops->transmit(reader, &apdu) == SC_SUCCESS && apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { if (!reader->version_major && !reader->version_minor) { unsigned int major = 0, minor = 0; /* add NUL termination, just in case... */ rbuf[apdu.resplen] = '\0'; sscanf((const char *) rbuf, "%u.%u", &major, &minor); reader->version_major = major>0xff ? 0xff : major; reader->version_minor = minor>0xff ? 0xff : minor; } } else { error++; } } if (error && reader) { sc_log(reader->ctx, "%d escape command%s failed, need to reset the card", error, error == 1 ? "" : "s"); if (reader->ops && reader->ops->transmit) { memset(&apdu, 0, sizeof(apdu)); apdu.cse = SC_APDU_CASE_3_SHORT; apdu.cla = 0x00; apdu.ins = 0xA4; apdu.p1 = 8; apdu.p2 = 0x0C; apdu.data = rbuf; rbuf[0] = 0x3F; rbuf[1] = 0x00; apdu.datalen = 2; apdu.lc = 2; apdu.resp = NULL; apdu.resplen = 0; apdu.le = 0; reader->ops->transmit(reader, &apdu); } } } OpenSC-0.26.1/src/libopensc/reader-tr03119.h000066400000000000000000000123101474147347300201430ustar00rootroot00000000000000/* * reader-tr03119.h: interface related to escape commands with pseudo APDUs * * 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 * 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 _READER_TR03119_H #define _READER_TR03119_H #include "libopensc/opensc.h" #include "libopensc/pace.h" #ifdef __cplusplus 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, const struct establish_pace_channel_input *input, unsigned char **asn1, size_t *asn1_len); int escape_buf_to_pace_input(sc_context_t *ctx, const unsigned char *asn1, size_t asn1_len, struct establish_pace_channel_input *input); int escape_pace_output_to_buf(sc_context_t *ctx, const struct establish_pace_channel_output *output, unsigned char **asn1, size_t *asn1_len); int escape_buf_to_pace_output(sc_context_t *ctx, const unsigned char *asn1, size_t asn1_len, struct establish_pace_channel_output *output); int escape_pace_capabilities_to_buf(sc_context_t *ctx, const unsigned long sc_reader_t_capabilities, unsigned char **asn1, size_t *asn1_len); int escape_buf_to_pace_capabilities(sc_context_t *ctx, const unsigned char *asn1, size_t asn1_len, unsigned long *sc_reader_t_capabilities); #ifdef __cplusplus } #endif #endif OpenSC-0.26.1/src/libopensc/sc-ossl-compat.h000066400000000000000000000102701474147347300205270ustar00rootroot00000000000000/* * sc-ossl-compat.h: OpenSC compatibility for older OpenSSL versions * * Copyright (C) 2016 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 * 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_OSSL_COMPAT_H #define _SC_OSSL_COMPAT_H #ifdef ENABLE_OPENSSL #ifdef __cplusplus extern "C" { #endif #include #include /* * 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 0x3040200fL (changes with its versions) */ #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_MD_CTX_md_data(x) (x->md_data) #endif #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 /* 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 >= 0x30000000L #define USE_OPENSSL3_LIBCTX #include #include #include typedef struct ossl3ctx { OSSL_LIB_CTX *libctx; OSSL_PROVIDER *defprov; OSSL_PROVIDER *legacyprov; } ossl3ctx_t; static inline EVP_MD *_sc_evp_md(ossl3ctx_t *ctx, const char *algorithm) { return EVP_MD_fetch(ctx->libctx, algorithm, NULL); } #define sc_evp_md(ctx, alg) _sc_evp_md((ctx)->ossl3ctx, alg) static inline void sc_evp_md_free(EVP_MD *md) { EVP_MD_free(md); } 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) 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 inline void sc_evp_cipher_free(EVP_CIPHER *cipher) { EVP_CIPHER_free(cipher); } #else /* OPENSSL < 3 */ #include static inline EVP_MD *sc_evp_md(void *unused, const char *algorithm) { return (EVP_MD *)EVP_get_digestbyname(algorithm); } static inline void sc_evp_md_free(EVP_MD *md) { return; } static inline EVP_PKEY_CTX *sc_evp_pkey_ctx_new(void *unused, EVP_PKEY *pkey) { return EVP_PKEY_CTX_new(pkey, NULL); } static inline EVP_CIPHER *sc_evp_cipher(void *unused, const char *algorithm) { return (EVP_CIPHER *)EVP_get_cipherbyname(algorithm); } static inline void sc_evp_cipher_free(EVP_CIPHER *cipher) { return; } #endif #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* ENABLE_OPENSSL */ #endif /* _SC_OSSL_COMPAT_H */ OpenSC-0.26.1/src/libopensc/sc.c000066400000000000000000000610651474147347300162730ustar00rootroot00000000000000/* * sc.c: General functions * * Copyright (C) 2001, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #ifdef HAVE_SYS_MMAN_H #include #endif #ifdef ENABLE_OPENSSL #include /* for OPENSSL_cleanse */ #endif #include "internal.h" #ifdef PACKAGE_VERSION static const char *sc_version = PACKAGE_VERSION; #else 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; } int sc_hex_to_bin(const char *in, u8 *out, size_t *outlen) { const char *sc_hex_to_bin_separators = " :"; if (in == NULL || out == NULL || outlen == NULL) { return SC_ERROR_INVALID_ARGUMENTS; } 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; } r = SC_ERROR_INVALID_ARGUMENTS; goto err; } if (byte_needs_nibble) { byte |= nibble; *out++ = (u8) byte; left--; byte_needs_nibble = 0; } else { byte = nibble << 4; byte_needs_nibble = 1; } } 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; } /* 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) { 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; } 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; } /* * Right trim all non-printable characters */ size_t sc_right_trim(u8 *buf, size_t len) { size_t i; if (!buf) return 0; if (len > 0) { for(i = len-1; i > 0; i--) { if(!isprint(buf[i])) { buf[i] = '\0'; len--; continue; } break; } } return len; } u8 *ulong2bebytes(u8 *buf, unsigned long x) { if (buf != NULL) { buf[3] = (u8) (x & 0xff); buf[2] = (u8) ((x >> 8) & 0xff); buf[1] = (u8) ((x >> 16) & 0xff); buf[0] = (u8) ((x >> 24) & 0xff); } return buf; } u8 *ushort2bebytes(u8 *buf, unsigned short x) { if (buf != NULL) { buf[1] = (u8) (x & 0xff); buf[0] = (u8) ((x >> 8) & 0xff); } return buf; } unsigned long bebytes2ulong(const u8 *buf) { if (buf == NULL) return 0UL; 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 | (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]; } 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) { int ii; if (!oid) return; for (ii=0; iivalue[ii] = -1; } int sc_format_oid(struct sc_object_id *oid, const char *in) { int ii, ret = SC_ERROR_INVALID_ARGUMENTS; const char *p; char *q; if (oid == NULL || in == NULL) return SC_ERROR_INVALID_ARGUMENTS; sc_init_oid(oid); p = in; for (ii=0; ii < SC_MAX_OBJECT_ID_OCTETS; ii++) { oid->value[ii] = (int)strtol(p, &q, 10); if (!*q) break; if (!(q[0] == '.' && isdigit((unsigned char)q[1]))) goto out; p = q + 1; } if (!sc_valid_oid(oid)) goto out; ret = SC_SUCCESS; out: if (ret) sc_init_oid(oid); return ret; } int sc_compare_oid(const struct sc_object_id *oid1, const struct sc_object_id *oid2) { int i; if (oid1 == NULL || oid2 == NULL) { return SC_ERROR_INVALID_ARGUMENTS; } for (i = 0; i < SC_MAX_OBJECT_ID_OCTETS; i++) { if (oid1->value[i] != oid2->value[i]) return 0; if (oid1->value[i] == -1) break; } return 1; } int sc_valid_oid(const struct sc_object_id *oid) { int ii; if (!oid) return 0; if (oid->value[0] == -1 || oid->value[1] == -1) return 0; if (oid->value[0] > 2 || oid->value[1] > 39) return 0; for (ii=0;iivalue[ii]) break; if (ii==SC_MAX_OBJECT_ID_OCTETS) return 0; return 1; } int sc_detect_card_presence(sc_reader_t *reader) { int r; LOG_FUNC_CALLED(reader->ctx); if (reader->ops->detect_card_presence == NULL) 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); } int sc_path_set(sc_path_t *path, int type, const u8 *id, size_t id_len, int idx, int count) { if (path == NULL || id == NULL || id_len == 0 || id_len > SC_MAX_PATH_SIZE) return SC_ERROR_INVALID_ARGUMENTS; memset(path, 0, sizeof(*path)); memcpy(path->value, id, id_len); path->len = id_len; path->type = type; path->index = idx; path->count = count; return SC_SUCCESS; } void sc_format_path(const char *str, sc_path_t *path) { int type = SC_PATH_TYPE_PATH; if (path) { memset(path, 0, sizeof(*path)); if (*str == 'i' || *str == 'I') { type = SC_PATH_TYPE_FILE_ID; str++; } path->len = sizeof(path->value); if (sc_hex_to_bin(str, path->value, &path->len) >= 0) { path->type = type; } path->count = -1; } } int sc_append_path(sc_path_t *dest, const sc_path_t *src) { return sc_concatenate_path(dest, dest, src); } int sc_append_path_id(sc_path_t *dest, const u8 *id, size_t idlen) { if (dest->len + idlen > SC_MAX_PATH_SIZE) return SC_ERROR_INVALID_ARGUMENTS; memcpy(dest->value + dest->len, id, idlen); dest->len += idlen; return SC_SUCCESS; } int sc_append_file_id(sc_path_t *dest, unsigned int fid) { u8 id[2] = { fid >> 8, fid & 0xff }; return sc_append_path_id(dest, id, 2); } int sc_concatenate_path(sc_path_t *d, const sc_path_t *p1, const sc_path_t *p2) { sc_path_t tpath; if (d == NULL || p1 == NULL || p2 == NULL) return SC_ERROR_INVALID_ARGUMENTS; if (p1->type == SC_PATH_TYPE_DF_NAME || p2->type == SC_PATH_TYPE_DF_NAME) /* we do not support concatenation of AIDs at the moment */ return SC_ERROR_NOT_SUPPORTED; if (p1->len + p2->len > SC_MAX_PATH_SIZE) return SC_ERROR_INVALID_ARGUMENTS; memset(&tpath, 0, sizeof(sc_path_t)); memcpy(tpath.value, p1->value, p1->len); memcpy(tpath.value + p1->len, p2->value, p2->len); tpath.len = p1->len + p2->len; tpath.type = SC_PATH_TYPE_PATH; /* use 'index' and 'count' entry of the second path object */ tpath.index = p2->index; tpath.count = p2->count; /* the result is currently always as path */ tpath.type = SC_PATH_TYPE_PATH; *d = tpath; return SC_SUCCESS; } const char *sc_print_path(const sc_path_t *path) { static char buffer[SC_MAX_PATH_STRING_SIZE + SC_MAX_AID_STRING_SIZE]; if (sc_path_print(buffer, sizeof(buffer), path) != SC_SUCCESS) buffer[0] = '\0'; return buffer; } int sc_path_print(char *buf, size_t buflen, const sc_path_t *path) { size_t i; if (buf == NULL || path == NULL) return SC_ERROR_INVALID_ARGUMENTS; if (buflen < path->len * 2 + path->aid.len * 2 + 3) return SC_ERROR_BUFFER_TOO_SMALL; buf[0] = '\0'; if (path->aid.len) { for (i = 0; i < path->aid.len; i++) snprintf(buf + strlen(buf), buflen - strlen(buf), "%02x", path->aid.value[i]); snprintf(buf + strlen(buf), buflen - strlen(buf), "::"); } for (i = 0; i < path->len; i++) snprintf(buf + strlen(buf), buflen - strlen(buf), "%02x", path->value[i]); if (!path->aid.len && path->type == SC_PATH_TYPE_DF_NAME) snprintf(buf + strlen(buf), buflen - strlen(buf), "::"); return SC_SUCCESS; } int sc_compare_path(const sc_path_t *path1, const sc_path_t *path2) { return path1->len == path2->len && !memcmp(path1->value, path2->value, path1->len); } int sc_compare_path_prefix(const sc_path_t *prefix, const sc_path_t *path) { sc_path_t tpath; if (prefix->len > path->len) return 0; tpath = *path; tpath.len = prefix->len; return sc_compare_path(&tpath, prefix); } const sc_path_t *sc_get_mf_path(void) { static const sc_path_t mf_path = { {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} }; return &mf_path; } int sc_file_add_acl_entry(sc_file_t *file, unsigned int operation, unsigned int method, unsigned long key_ref) { sc_acl_entry_t *p, *_new; if (file == NULL || operation >= SC_MAX_AC_OPS) { return SC_ERROR_INVALID_ARGUMENTS; } switch (method) { case SC_AC_NEVER: sc_file_clear_acl_entries(file, operation); file->acl[operation] = (sc_acl_entry_t *) 1; return SC_SUCCESS; case SC_AC_NONE: sc_file_clear_acl_entries(file, operation); file->acl[operation] = (sc_acl_entry_t *) 2; return SC_SUCCESS; case SC_AC_UNKNOWN: sc_file_clear_acl_entries(file, operation); file->acl[operation] = (sc_acl_entry_t *) 3; return SC_SUCCESS; default: /* NONE and UNKNOWN get zapped when a new AC is added. * If the ACL is NEVER, additional entries will be * dropped silently. */ if (file->acl[operation] == (sc_acl_entry_t *) 1) return SC_SUCCESS; if (file->acl[operation] == (sc_acl_entry_t *) 2 || file->acl[operation] == (sc_acl_entry_t *) 3) file->acl[operation] = NULL; } /* If the entry is already present (e.g. due to the mapping) * of the card's AC with OpenSC's), don't add it again. */ for (p = file->acl[operation]; p != NULL; p = p->next) { if ((p->method == method) && (p->key_ref == key_ref)) return SC_SUCCESS; } _new = malloc(sizeof(sc_acl_entry_t)); if (_new == NULL) return SC_ERROR_OUT_OF_MEMORY; _new->method = method; _new->key_ref = (unsigned)key_ref; _new->next = NULL; p = file->acl[operation]; if (p == NULL) { file->acl[operation] = _new; return SC_SUCCESS; } while (p->next != NULL) p = p->next; p->next = _new; return SC_SUCCESS; } const sc_acl_entry_t * sc_file_get_acl_entry(const sc_file_t *file, unsigned int operation) { sc_acl_entry_t *p; static const sc_acl_entry_t e_never = { 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, NULL }; static const sc_acl_entry_t e_unknown = { SC_AC_UNKNOWN, SC_AC_KEY_REF_NONE, NULL }; if (file == NULL || operation >= SC_MAX_AC_OPS) { return NULL; } p = file->acl[operation]; if (p == (sc_acl_entry_t *) 1) return &e_never; if (p == (sc_acl_entry_t *) 2) return &e_none; if (p == (sc_acl_entry_t *) 3) return &e_unknown; return file->acl[operation]; } void sc_file_clear_acl_entries(sc_file_t *file, unsigned int operation) { sc_acl_entry_t *e; if (file == NULL || operation >= SC_MAX_AC_OPS) { return; } e = file->acl[operation]; if (e == (sc_acl_entry_t *) 1 || e == (sc_acl_entry_t *) 2 || e == (sc_acl_entry_t *) 3) { file->acl[operation] = NULL; return; } while (e != NULL) { sc_acl_entry_t *tmp = e->next; free(e); e = tmp; } file->acl[operation] = NULL; } sc_file_t * sc_file_new(void) { sc_file_t *file = (sc_file_t *)calloc(1, sizeof(sc_file_t)); if (file == NULL) return NULL; file->magic = SC_FILE_MAGIC; return file; } void sc_file_free(sc_file_t *file) { unsigned int i; if (file == NULL || !sc_file_valid(file)) return; file->magic = 0; for (i = 0; i < SC_MAX_AC_OPS; i++) sc_file_clear_acl_entries(file, i); if (file->sec_attr) free(file->sec_attr); if (file->prop_attr) free(file->prop_attr); if (file->type_attr) free(file->type_attr); if (file->encoded_content) free(file->encoded_content); free(file); } void sc_file_dup(sc_file_t **dest, const sc_file_t *src) { sc_file_t *newf; const sc_acl_entry_t *e; unsigned int op; *dest = NULL; if (!sc_file_valid(src)) return; newf = sc_file_new(); if (newf == NULL) return; *dest = newf; memcpy(&newf->path, &src->path, sizeof(struct sc_path)); memcpy(&newf->name, &src->name, sizeof(src->name)); newf->namelen = src->namelen; newf->type = src->type; newf->shareable = src->shareable; newf->ef_structure = src->ef_structure; newf->size = src->size; newf->id = src->id; newf->status = src->status; for (op = 0; op < SC_MAX_AC_OPS; op++) { newf->acl[op] = NULL; e = sc_file_get_acl_entry(src, op); if (e != NULL) { if (sc_file_add_acl_entry(newf, op, e->method, e->key_ref) < 0) goto err; } } newf->record_length = src->record_length; newf->record_count = src->record_count; if (sc_file_set_sec_attr(newf, src->sec_attr, src->sec_attr_len) < 0) goto err; if (sc_file_set_prop_attr(newf, src->prop_attr, src->prop_attr_len) < 0) goto err; if (sc_file_set_type_attr(newf, src->type_attr, src->type_attr_len) < 0) goto err; if (sc_file_set_content(newf, src->encoded_content, src->encoded_content_len) < 0) goto err; return; err: sc_file_free(newf); *dest = NULL; } int sc_file_set_sec_attr(sc_file_t *file, const u8 *sec_attr, size_t sec_attr_len) { u8 *tmp; if (!sc_file_valid(file)) { return SC_ERROR_INVALID_ARGUMENTS; } if (sec_attr == NULL || sec_attr_len == 0) { if (file->sec_attr != NULL) free(file->sec_attr); file->sec_attr = NULL; file->sec_attr_len = 0; return 0; } tmp = (u8 *) realloc(file->sec_attr, sec_attr_len); if (!tmp) { if (file->sec_attr) free(file->sec_attr); file->sec_attr = NULL; file->sec_attr_len = 0; return SC_ERROR_OUT_OF_MEMORY; } file->sec_attr = tmp; memcpy(file->sec_attr, sec_attr, sec_attr_len); file->sec_attr_len = sec_attr_len; return 0; } int sc_file_set_prop_attr(sc_file_t *file, const u8 *prop_attr, size_t prop_attr_len) { u8 *tmp; if (!sc_file_valid(file)) { return SC_ERROR_INVALID_ARGUMENTS; } if (prop_attr == NULL || prop_attr_len == 0) { if (file->prop_attr != NULL) free(file->prop_attr); file->prop_attr = NULL; file->prop_attr_len = 0; return SC_SUCCESS; } tmp = (u8 *) realloc(file->prop_attr, prop_attr_len); if (!tmp) { if (file->prop_attr) free(file->prop_attr); file->prop_attr = NULL; file->prop_attr_len = 0; return SC_ERROR_OUT_OF_MEMORY; } file->prop_attr = tmp; memcpy(file->prop_attr, prop_attr, prop_attr_len); file->prop_attr_len = prop_attr_len; return SC_SUCCESS; } int sc_file_set_type_attr(sc_file_t *file, const u8 *type_attr, size_t type_attr_len) { u8 *tmp; if (!sc_file_valid(file)) { return SC_ERROR_INVALID_ARGUMENTS; } if (type_attr == NULL || type_attr_len == 0) { if (file->type_attr != NULL) free(file->type_attr); file->type_attr = NULL; file->type_attr_len = 0; return SC_SUCCESS; } tmp = (u8 *) realloc(file->type_attr, type_attr_len); if (!tmp) { if (file->type_attr) free(file->type_attr); file->type_attr = NULL; file->type_attr_len = 0; return SC_ERROR_OUT_OF_MEMORY; } file->type_attr = tmp; memcpy(file->type_attr, type_attr, type_attr_len); file->type_attr_len = type_attr_len; return SC_SUCCESS; } int sc_file_set_content(sc_file_t *file, const u8 *content, size_t content_len) { u8 *tmp; if (!sc_file_valid(file)) { return SC_ERROR_INVALID_ARGUMENTS; } if (content == NULL || content_len == 0) { if (file->encoded_content != NULL) free(file->encoded_content); file->encoded_content = NULL; file->encoded_content_len = 0; return SC_SUCCESS; } tmp = (u8 *) realloc(file->encoded_content, content_len); if (!tmp) { if (file->encoded_content) free(file->encoded_content); file->encoded_content = NULL; file->encoded_content_len = 0; return SC_ERROR_OUT_OF_MEMORY; } file->encoded_content = tmp; memcpy(file->encoded_content, content, content_len); file->encoded_content_len = content_len; return SC_SUCCESS; } int sc_file_valid(const sc_file_t *file) { if (file == NULL) return 0; return file->magic == SC_FILE_MAGIC; } int _sc_parse_atr(sc_reader_t *reader) { u8 *p = reader->atr.value; int atr_len = (int) reader->atr.len; int n_hist, x; int tx[4] = {-1, -1, -1, -1}; int i, FI, DI; const int Fi_table[] = { 372, 372, 558, 744, 1116, 1488, 1860, -1, -1, 512, 768, 1024, 1536, 2048, -1, -1 }; const int f_table[] = { 40, 50, 60, 80, 120, 160, 200, -1, -1, 50, 75, 100, 150, 200, -1, -1 }; const int Di_table[] = { -1, 1, 2, 4, 8, 16, 32, -1, 12, 20, -1, -1, -1, -1, -1, -1 }; reader->atr_info.hist_bytes_len = 0; reader->atr_info.hist_bytes = NULL; if (atr_len == 0) { sc_log(reader->ctx, "empty ATR - card not present?\n"); return SC_ERROR_INTERNAL; } if (p[0] != 0x3B && p[0] != 0x3F) { sc_log(reader->ctx, "invalid sync byte in ATR: 0x%02X\n", p[0]); return SC_ERROR_INTERNAL; } n_hist = p[1] & 0x0F; x = p[1] >> 4; p += 2; atr_len -= 2; for (i = 0; i < 4 && atr_len > 0; i++) { if (x & (1 << i)) { tx[i] = *p; p++; atr_len--; } else tx[i] = -1; } if (tx[0] >= 0) { reader->atr_info.FI = FI = tx[0] >> 4; reader->atr_info.DI = DI = tx[0] & 0x0F; reader->atr_info.Fi = Fi_table[FI]; reader->atr_info.f = f_table[FI]; reader->atr_info.Di = Di_table[DI]; } else { reader->atr_info.Fi = -1; reader->atr_info.f = -1; reader->atr_info.Di = -1; } if (tx[2] >= 0) reader->atr_info.N = tx[3]; else reader->atr_info.N = -1; while (tx[3] > 0 && tx[3] & 0xF0 && atr_len > 0) { x = tx[3] >> 4; for (i = 0; i < 4 && atr_len > 0; i++) { if (x & (1 << i)) { tx[i] = *p; p++; atr_len--; } else tx[i] = -1; } } if (atr_len <= 0) return SC_SUCCESS; if (n_hist > atr_len) n_hist = atr_len; reader->atr_info.hist_bytes_len = n_hist; reader->atr_info.hist_bytes = p; return SC_SUCCESS; } void *sc_mem_secure_alloc(size_t len) { void *p; #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 VirtualUnlock(ptr, len); VirtualFree(ptr, 0, MEM_RELEASE); #else munlock(ptr, len); munmap(ptr, len); #endif } void sc_mem_clear(void *ptr, size_t len) { if (len > 0) { #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); #endif } } int sc_mem_reverse(unsigned char *buf, size_t len) { unsigned char ch; size_t ii; if (!buf || !len) return SC_ERROR_INVALID_ARGUMENTS; for (ii = 0; ii < len / 2; ii++) { ch = *(buf + ii); *(buf + ii) = *(buf + len - 1 - ii); *(buf + len - 1 - ii) = ch; } return SC_SUCCESS; } static int sc_remote_apdu_allocate(struct sc_remote_data *rdata, struct sc_remote_apdu **new_rapdu) { struct sc_remote_apdu *rapdu = NULL, *rr; if (!rdata) return SC_ERROR_INVALID_ARGUMENTS; rapdu = calloc(1, sizeof(struct sc_remote_apdu)); if (rapdu == NULL) return SC_ERROR_OUT_OF_MEMORY; rapdu->apdu.data = &rapdu->sbuf[0]; rapdu->apdu.resp = &rapdu->rbuf[0]; rapdu->apdu.resplen = sizeof(rapdu->rbuf); if (new_rapdu) *new_rapdu = rapdu; if (rdata->data == NULL) { rdata->data = rapdu; rdata->length = 1; return SC_SUCCESS; } for (rr = rdata->data; rr->next; rr = rr->next) ; rr->next = rapdu; rdata->length++; return SC_SUCCESS; } static void sc_remote_apdu_free (struct sc_remote_data *rdata) { struct sc_remote_apdu *rapdu = NULL; if (!rdata) return; rapdu = rdata->data; while(rapdu) { struct sc_remote_apdu *rr = rapdu->next; free(rapdu); rapdu = rr; } } void sc_remote_data_init(struct sc_remote_data *rdata) { if (!rdata) return; memset(rdata, 0, sizeof(struct sc_remote_data)); rdata->alloc = sc_remote_apdu_allocate; rdata->free = sc_remote_apdu_free; } static unsigned long sc_CRC_tab32[256]; static int sc_CRC_tab32_initialized = 0; unsigned sc_crc32(const unsigned char *value, size_t len) { size_t ii, jj; unsigned long crc; unsigned long index, long_c; if (!sc_CRC_tab32_initialized) { for (ii=0; ii<256; ii++) { crc = (unsigned long) ii; for (jj=0; jj<8; jj++) { if ( crc & 0x00000001L ) crc = ( crc >> 1 ) ^ 0xEDB88320l; else crc = crc >> 1; } sc_CRC_tab32[ii] = crc; } sc_CRC_tab32_initialized = 1; } crc = 0xffffffffL; for (ii=0; ii> 8) ^ sc_CRC_tab32[ index & 0xff ]; } crc ^= 0xffffffff; 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++) { if ((buf[idx] & 0xF0) == plain_tag && idx + expected_len < len && (expected_len == 0 || expected_len == (buf[idx] & 0x0F))) { if (outlen != NULL) *outlen = buf[idx] & 0x0F; return buf + (idx + 1); } idx += (buf[idx] & 0x0F); } } return NULL; } /**************************** mutex functions ************************/ int sc_mutex_create(const sc_context_t *ctx, void **mutex) { if (ctx == NULL) return SC_ERROR_INVALID_ARGUMENTS; if (ctx->thread_ctx != NULL && ctx->thread_ctx->create_mutex != NULL) return ctx->thread_ctx->create_mutex(mutex); else return SC_SUCCESS; } int sc_mutex_lock(const sc_context_t *ctx, void *mutex) { if (ctx == NULL) return SC_ERROR_INVALID_ARGUMENTS; if (ctx->thread_ctx != NULL && ctx->thread_ctx->lock_mutex != NULL) return ctx->thread_ctx->lock_mutex(mutex); else return SC_SUCCESS; } int sc_mutex_unlock(const sc_context_t *ctx, void *mutex) { if (ctx == NULL) return SC_ERROR_INVALID_ARGUMENTS; if (ctx->thread_ctx != NULL && ctx->thread_ctx->unlock_mutex != NULL) return ctx->thread_ctx->unlock_mutex(mutex); else return SC_SUCCESS; } int sc_mutex_destroy(const sc_context_t *ctx, void *mutex) { if (ctx == NULL) return SC_ERROR_INVALID_ARGUMENTS; if (ctx->thread_ctx != NULL && ctx->thread_ctx->destroy_mutex != NULL) return ctx->thread_ctx->destroy_mutex(mutex); else return SC_SUCCESS; } unsigned long sc_thread_id(const sc_context_t *ctx) { if (ctx == NULL || ctx->thread_ctx == NULL || ctx->thread_ctx->thread_id == NULL) return 0UL; else return ctx->thread_ctx->thread_id(); } void sc_free(void *p) { free(p); } OpenSC-0.26.1/src/libopensc/sec.c000066400000000000000000000240061474147347300164320ustar00rootroot00000000000000/* * sec.c: Cryptography and security (ISO7816-8) functions * * Copyright (C) 2001, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include "internal.h" int sc_decipher(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; } 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); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } int sc_compute_signature(sc_card_t *card, const u8 * data, size_t datalen, u8 * out, size_t outlen) { int r; if (card == NULL) { return SC_ERROR_INVALID_ARGUMENTS; } 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) { int r; if (card == NULL) { return SC_ERROR_INVALID_ARGUMENTS; } 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); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } int sc_restore_security_env(sc_card_t *card, int se_num) { int r; if (card == NULL) { return SC_ERROR_INVALID_ARGUMENTS; } 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, const u8 *pin, size_t pinlen, int *tries_left) { struct sc_pin_cmd_data data; memset(&data, 0, sizeof(data)); data.cmd = SC_PIN_CMD_VERIFY; data.pin_type = type; data.pin_reference = ref; data.pin1.data = pin; data.pin1.len = pinlen; return sc_pin_cmd(card, &data, tries_left); } int sc_logout(sc_card_t *card) { if (card->ops->logout == NULL) return SC_ERROR_NOT_SUPPORTED; return card->ops->logout(card); } int sc_change_reference_data(sc_card_t *card, unsigned int type, int ref, const u8 *old, size_t oldlen, const u8 *newref, size_t newlen, int *tries_left) { struct sc_pin_cmd_data data; memset(&data, 0, sizeof(data)); data.cmd = SC_PIN_CMD_CHANGE; data.pin_type = type; data.pin_reference = ref; data.pin1.data = old; data.pin1.len = oldlen; data.pin2.data = newref; data.pin2.len = newlen; return sc_pin_cmd(card, &data, tries_left); } int sc_reset_retry_counter(sc_card_t *card, unsigned int type, int ref, const u8 *puk, size_t puklen, const u8 *newref, size_t newlen) { struct sc_pin_cmd_data data; memset(&data, 0, sizeof(data)); data.cmd = SC_PIN_CMD_UNBLOCK; data.pin_type = type; data.pin_reference = ref; data.pin1.data = puk; data.pin1.len = puklen; data.pin2.data = newref; data.pin2.len = newlen; return sc_pin_cmd(card, &data, NULL); } /* * This is the new style pin command, which takes care of all PIN * operations. * If a PIN was given by the application, the card driver should * 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 r, debug; if (card == NULL) { return SC_ERROR_INVALID_ARGUMENTS; } 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); } else if (!(data->flags & SC_PIN_CMD_USE_PINPAD)) { /* Card driver doesn't support new style pin_cmd, fall * back to old interface */ r = SC_ERROR_NOT_SUPPORTED; switch (data->cmd) { case SC_PIN_CMD_VERIFY: if (card->ops->verify != NULL) r = card->ops->verify(card, data->pin_type, data->pin_reference, data->pin1.data, (size_t) data->pin1.len, tries_left); break; case SC_PIN_CMD_CHANGE: if (card->ops->change_reference_data != NULL) r = card->ops->change_reference_data(card, data->pin_type, data->pin_reference, data->pin1.data, (size_t) data->pin1.len, data->pin2.data, (size_t) data->pin2.len, tries_left); break; case SC_PIN_CMD_UNBLOCK: if (card->ops->reset_retry_counter != NULL) r = card->ops->reset_retry_counter(card, data->pin_type, data->pin_reference, data->pin1.data, (size_t) data->pin1.len, data->pin2.data, (size_t) data->pin2.len); break; } if (r == SC_ERROR_NOT_SUPPORTED) sc_log(card->ctx, "unsupported PIN operation (%d)", data->cmd); } else { 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); } /* * This function will copy a PIN, convert and pad it as required * * Note about the SC_PIN_ENCODING_GLP encoding: * 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 = {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 */ int sc_build_pin(u8 *buf, size_t buflen, struct sc_pin_cmd_pin *pin, int pad) { size_t i = 0, j, pin_len = pin->len; if (pin->max_length && pin_len > pin->max_length) return SC_ERROR_INVALID_ARGUMENTS; if (pin->encoding == SC_PIN_ENCODING_GLP) { while (pin_len > 0 && pin->data[pin_len - 1] == 0xFF) pin_len--; if (pin_len > 12) return SC_ERROR_INVALID_ARGUMENTS; for (i = 0; i < pin_len; i++) { if (pin->data[i] < '0' || pin->data[i] > '9') return SC_ERROR_INVALID_ARGUMENTS; } buf[0] = 0x20 | (u8) pin_len; buf++; buflen--; } /* PIN given by application, encode if required */ if (pin->encoding == SC_PIN_ENCODING_ASCII) { if (pin_len > buflen) return SC_ERROR_BUFFER_TOO_SMALL; memcpy(buf, pin->data, pin_len); i = pin_len; } else if (pin->encoding == SC_PIN_ENCODING_BCD || pin->encoding == SC_PIN_ENCODING_GLP) { 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) i++; } if (j & 1) { buf[i] <<= 4; buf[i] |= pin->pad_char & 0xf; i++; } } /* Pad to maximum PIN length if requested */ if (pad || pin->encoding == SC_PIN_ENCODING_GLP) { size_t pad_length = pin->pad_length; u8 pad_char = pin->encoding == SC_PIN_ENCODING_GLP ? 0xFF : pin->pad_char; if (pin->encoding == SC_PIN_ENCODING_BCD) pad_length >>= 1; if (pin->encoding == SC_PIN_ENCODING_GLP) pad_length = 8; if (pad_length > buflen) return SC_ERROR_BUFFER_TOO_SMALL; if (pad_length && i < pad_length) { memset(buf + i, pad_char, pad_length - i); i = pad_length; } } 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); } OpenSC-0.26.1/src/libopensc/simpletlv.c000066400000000000000000000052211474147347300176750ustar00rootroot00000000000000/* * simpletlv.c: Simple TLV encoding and decoding functions * * Copyright (C) 2016 Red Hat, Inc. * * Authors: Robert Relyea * 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 #include "internal.h" #include "simpletlv.h" /* * Put a tag/length record to a file in Simple TLV based on the datalen * content length. */ int sc_simpletlv_put_tag(u8 tag, size_t datalen, u8 *out, size_t outlen, u8 **ptr) { u8 *p = out; if (outlen < 2 || (outlen < 4 && datalen >= 0xff)) return SC_ERROR_INVALID_ARGUMENTS; /* 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 { /* longer values up to 65535 */ *p++ = (u8)0xff; /* first byte is 0xff */ *p++ = (u8)datalen & 0xff; *p++ = (u8)(datalen >> 8) & 0xff; /* LE */ } if (ptr != NULL) *ptr = p; return SC_SUCCESS; } /* Read the TL file and return appropriate tag and the length of associated * content. */ int sc_simpletlv_read_tag(const u8 **buf, size_t buflen, u8 *tag_out, size_t *taglen) { u8 tag; size_t left = buflen, len; const u8 *p = *buf; *buf = NULL; if (left < 2) { return SC_ERROR_INVALID_TLV_OBJECT; } tag = *p; p++; len = *p; p++; left -= 2; if (len == 0xff) { /* don't crash on bad data */ if (left < 2) { return SC_ERROR_INVALID_TLV_OBJECT; } /* skip two bytes (the size) */ len = lebytes2ushort(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; } OpenSC-0.26.1/src/libopensc/simpletlv.h000066400000000000000000000035571474147347300177140ustar00rootroot00000000000000/* * simpletlv.h: Simple TLV header file * * Copyright (C) 2016 Red Hat, Inc. * * Authors: Robert Relyea * 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 _OPENSC_SIMPLETLV_H #define _OPENSC_SIMPLETLV_H #ifdef __cplusplus extern "C" { #endif #include "libopensc/opensc.h" /* * Create a tag/length file in Simple TLV based on the val_len content length * @param tag Tag to store into the TL file * @param datalen Data length to store into the TL file * @param out TL byte array to write into * @param outlen The length of the output array * @param ptr The end of the TL record written * @return SC_SUCCESS for correct input */ int sc_simpletlv_put_tag(u8 tag, size_t datalen, u8 *out, size_t outlen, u8 **ptr); /* get the Simple TLV tag and length. * @param buf Pointer to the TL file * @param buflen The length of TL file * @param tag_out The tag from the TL file * @param taglen The length of the V record * @return SC_SUCCESS on valid input */ int sc_simpletlv_read_tag(const u8 **buf, size_t buflen, u8 *tag_out, size_t *taglen); #endif OpenSC-0.26.1/src/libopensc/sm.c000066400000000000000000000141001474147347300162710ustar00rootroot00000000000000/* * sm.c: Secure Messaging helper functions * * Copyright (C) 2013 Viktor Tarasov * * 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" #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 }, { "mac", SC_ASN1_OCTET_STRING, SC_ASN1_CTX | 0x0E, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; int sc_sm_parse_answer(struct sc_card *card, unsigned char *resp_data, size_t resp_len, struct sm_card_response *out) { struct sc_asn1_entry asn1_sm_response[4]; unsigned char data[SC_MAX_APDU_BUFFER_SIZE]; size_t data_len = sizeof(data); unsigned char status[2] = {0, 0}; size_t status_len = sizeof(status); unsigned char mac[8]; size_t mac_len = sizeof(mac); int rv; if (!resp_data || !resp_len || !out) return SC_ERROR_INVALID_ARGUMENTS; sc_copy_asn1_entry(c_asn1_sm_response, asn1_sm_response); sc_format_asn1_entry(asn1_sm_response + 0, data, &data_len, 0); sc_format_asn1_entry(asn1_sm_response + 1, status, &status_len, 0); sc_format_asn1_entry(asn1_sm_response + 2, mac, &mac_len, 0); rv = sc_asn1_decode(card->ctx, asn1_sm_response, resp_data, resp_len, NULL, NULL); if (rv) return rv; if (asn1_sm_response[0].flags & SC_ASN1_PRESENT) { if (data_len > sizeof(out->data)) return SC_ERROR_BUFFER_TOO_SMALL; memcpy(out->data, data, data_len); out->data_len = data_len; } if (asn1_sm_response[1].flags & SC_ASN1_PRESENT) { if (!status[0]) return SC_ERROR_INVALID_DATA; out->sw1 = status[0]; out->sw2 = status[1]; } if (asn1_sm_response[2].flags & SC_ASN1_PRESENT) { memcpy(out->mac, mac, mac_len); out->mac_len = mac_len; } return SC_SUCCESS; } /** parse answer of SM protected APDU returned by APDU or by 'GET RESPONSE' * @param card 'sc_card' smartcard object * @param resp_data 'raw data returned by SM protected APDU * @param resp_len 'length of raw data returned by SM protected APDU * @param ref_rv 'status word returned by APDU or 'GET RESPONSE' (can be different from status word encoded into SM response date) * @param apdu 'sc_apdu' object to update * @return SC_SUCCESS on success and an error code otherwise */ int sc_sm_update_apdu_response(struct sc_card *card, unsigned char *resp_data, size_t resp_len, int ref_rv, struct sc_apdu *apdu) { struct sm_card_response sm_resp; int r; if (!apdu) return SC_ERROR_INVALID_ARGUMENTS; else if (!resp_data || !resp_len) return SC_SUCCESS; memset(&sm_resp, 0, sizeof(sm_resp)); r = sc_sm_parse_answer(card, resp_data, resp_len, &sm_resp); if (r) return r; if (sm_resp.mac_len) { if (sm_resp.mac_len > sizeof(apdu->mac)) return SC_ERROR_INVALID_DATA; memcpy(apdu->mac, sm_resp.mac, sm_resp.mac_len); apdu->mac_len = sm_resp.mac_len; } apdu->sw1 = sm_resp.sw1; apdu->sw2 = sm_resp.sw2; return SC_SUCCESS; } int sc_sm_single_transmit(struct sc_card *card, struct sc_apdu *apdu) { struct sc_context *ctx = card->ctx; struct sc_apdu *sm_apdu = NULL; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "SM_MODE:%X", card->sm_ctx.sm_mode); if (!card->sm_ctx.ops.get_sm_apdu || !card->sm_ctx.ops.free_sm_apdu) LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); /* get SM encoded APDU */ rv = card->sm_ctx.ops.get_sm_apdu(card, apdu, &sm_apdu); if (rv == SC_ERROR_SM_NOT_APPLIED) { /* SM wrap of this APDU is ignored by card driver. * Send plain APDU to the reader driver */ rv = card->reader->ops->transmit(card->reader, apdu); LOG_FUNC_RETURN(ctx, rv); } else { if (rv < 0) sc_sm_stop(card); } LOG_TEST_RET(ctx, rv, "get SM APDU error"); /* check if SM APDU is still valid */ rv = sc_check_apdu(card, sm_apdu); if (rv < 0) { card->sm_ctx.ops.free_sm_apdu(card, apdu, &sm_apdu); sc_sm_stop(card); LOG_TEST_RET(ctx, rv, "cannot validate SM encoded APDU"); } /* send APDU flagged as 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); sc_sm_stop(card); LOG_TEST_RET(ctx, rv, "unable to transmit APDU"); } /* decode SM answer and free temporary SM related data */ rv = card->sm_ctx.ops.free_sm_apdu(card, apdu, &sm_apdu); if (rv < 0) sc_sm_stop(card); LOG_FUNC_RETURN(ctx, rv); } int sc_sm_stop(struct sc_card *card) { int r = SC_SUCCESS; if (card) { if (card->sm_ctx.sm_mode == SM_MODE_TRANSMIT && card->sm_ctx.ops.close) r = card->sm_ctx.ops.close(card); card->sm_ctx.sm_mode = SM_MODE_NONE; } return r; } #else int sc_sm_parse_answer(struct sc_card *card, unsigned char *resp_data, size_t resp_len, struct sm_card_response *out) { return SC_ERROR_NOT_SUPPORTED; } int sc_sm_update_apdu_response(struct sc_card *card, unsigned char *resp_data, size_t resp_len, int ref_rv, struct sc_apdu *apdu) { return SC_ERROR_NOT_SUPPORTED; } int sc_sm_single_transmit(struct sc_card *card, struct sc_apdu *apdu) { return SC_ERROR_NOT_SUPPORTED; } int sc_sm_stop(struct sc_card *card) { return SC_ERROR_NOT_SUPPORTED; } #endif OpenSC-0.26.1/src/libopensc/sm.h000066400000000000000000000223301474147347300163020ustar00rootroot00000000000000/* * sm.h: Support of Secure Messaging * * Copyright (C) 2010 Viktor Tarasov * OpenTrust * * 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 _SM_H #define _SM_H #include #ifdef HAVE_UNISTD_H #include #endif #ifdef __cplusplus extern "C" { #endif #include #include #include #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 #define SM_CMD_MUTUAL_AUTHENTICATION 0x20 #define SM_CMD_RSA 0x100 #define SM_CMD_RSA_GENERATE 0x101 #define SM_CMD_RSA_UPDATE 0x102 #define SM_CMD_RSA_READ_PUBLIC 0x103 #define SM_CMD_FILE 0x200 #define SM_CMD_FILE_READ 0x201 #define SM_CMD_FILE_UPDATE 0x202 #define SM_CMD_FILE_CREATE 0x203 #define SM_CMD_FILE_DELETE 0x204 #define SM_CMD_PIN 0x300 #define SM_CMD_PIN_VERIFY 0x301 #define SM_CMD_PIN_RESET 0x302 #define SM_CMD_PIN_SET_PIN 0x303 #define SM_CMD_PSO 0x400 #define SM_CMD_PSO_DST 0x401 #define SM_CMD_APDU 0x500 #define SM_CMD_APDU_TRANSMIT 0x501 #define SM_CMD_APDU_RAW 0x502 #define SM_CMD_APPLET 0x600 #define SM_CMD_APPLET_DELETE 0x601 #define SM_CMD_APPLET_LOAD 0x602 #define SM_CMD_APPLET_INSTALL 0x603 #define SM_CMD_EXTERNAL_AUTH 0x700 #define SM_CMD_EXTERNAL_AUTH_INIT 0x701 #define SM_CMD_EXTERNAL_AUTH_CHALLENGE 0x702 #define SM_CMD_EXTERNAL_AUTH_DOIT 0x703 #define SM_CMD_SDO_UPDATE 0x800 #define SM_CMD_FINALIZE 0x900 #define SM_RESPONSE_CONTEXT_TAG 0xA1 #define SM_RESPONSE_CONTEXT_DATA_TAG 0xA2 #define SM_MAX_DATA_SIZE 0xE0 #define SM_SMALL_CHALLENGE_LEN 8 #define SM_GP_SECURITY_NO 0x00 #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 * Global Platform SM channel parameters */ struct sm_type_params_gp { unsigned level; unsigned index; unsigned version; struct sc_cplc cplc; }; /* * @struct sm_gp_keyset * Global Platform keyset: * - version, index; * - keyset presented in three parts: 'ENC', 'MAC' and 'KEK'; * - keyset presented in continuous manner - raw or 'to be diversified'. */ struct sm_gp_keyset { int version; int index; unsigned char enc[16]; unsigned char mac[16]; unsigned char kek[16]; unsigned char kmc[48]; unsigned kmc_len; }; /* * @struct sm_gp_session * Global Platform SM session data */ struct sm_gp_session { struct sm_gp_keyset gp_keyset; struct sm_type_params_gp params; unsigned char host_challenge[SM_SMALL_CHALLENGE_LEN]; unsigned char card_challenge[SM_SMALL_CHALLENGE_LEN]; unsigned char *session_enc, *session_mac, *session_kek; unsigned char mac_icv[8]; }; /* CWA, IAS/ECC data types */ /* * @struct sm_type_params_cwa */ struct sm_type_params_cwa { struct sc_crt crt_at; }; /* * @struct sm_cwa_keyset * CWA keyset: * - SDO reference; * - 'ENC' and 'MAC' 3DES keys. */ struct sm_cwa_keyset { unsigned sdo_reference; unsigned char enc[16]; unsigned char mac[16]; }; /* * @struct sm_cwa_token_data * CWA token data: * - serial; * - 'small' random; * - 'big' random. */ struct sm_cwa_token_data { unsigned char sn[8]; unsigned char rnd[8]; unsigned char k[32]; }; /* * @struct sm_cwa_session * CWA working SM session data: * - ICC and IFD token data; * - ENC and MAC session keys; * - SSC (SM Sequence Counter); * - 'mutual authentication' data. */ struct sm_cwa_session { struct sm_cwa_keyset cwa_keyset; struct sm_type_params_cwa params; struct sm_cwa_token_data icc; struct sm_cwa_token_data ifd; unsigned char session_enc[16]; unsigned char session_mac[16]; unsigned char ssc[8]; unsigned char host_challenge[SM_SMALL_CHALLENGE_LEN]; unsigned char card_challenge[SM_SMALL_CHALLENGE_LEN]; unsigned char mdata[0x48]; size_t mdata_len; }; /* * @struct sm_dh_session * DH SM session data: */ struct sm_dh_session { struct sc_tlv_data g; struct sc_tlv_data N; struct sc_tlv_data ifd_p; struct sc_tlv_data ifd_y; struct sc_tlv_data icc_p; struct sc_tlv_data shared_secret; unsigned char session_enc[16]; unsigned char session_mac[16]; unsigned char card_challenge[32]; unsigned char ssc[8]; }; /* * @struct sc_info is the * placehold for the secure messaging working data: * - SM type; * - SM session state; * - command to execute by external SM module; * - data related to the current card context. */ struct sm_info { char config_section[64]; unsigned card_type; unsigned cmd; void *cmd_data; unsigned sm_type; union { struct sm_gp_session gp; struct sm_cwa_session cwa; struct sm_dh_session dh; } session; struct sc_serial_number serialnr; unsigned security_condition; struct sc_path current_path_df; struct sc_path current_path_ef; struct sc_aid current_aid; unsigned char *rdata; size_t rdata_len; }; /* * @struct sm_card_response * data type to return card response. */ typedef struct sm_card_response { int num; unsigned char data[SC_MAX_APDU_BUFFER_SIZE]; size_t data_len; unsigned char mac[8]; size_t mac_len; unsigned char sw1, sw2; struct sm_card_response *next; struct sm_card_response *prev; } sm_card_response_t; struct sc_context; struct sc_card; /* * @struct sm_card_operations * card driver handlers related to secure messaging (in 'APDU TRANSMIT' mode) * - 'open' - initialize SM session; * - 'encode apdu' - SM encoding of the raw APDU; * - 'decrypt response' - decode card answer; * - 'close' - close SM session. */ struct sm_card_operations { int (*open)(struct sc_card *card); int (*get_sm_apdu)(struct sc_card *card, struct sc_apdu *apdu, struct sc_apdu **sm_apdu); int (*free_sm_apdu)(struct sc_card *card, struct sc_apdu *apdu, struct sc_apdu **sm_apdu); int (*close)(struct sc_card *card); int (*read_binary)(struct sc_card *card, unsigned int idx, unsigned char * buf, size_t count); int (*update_binary)(struct sc_card *card, unsigned int idx, const unsigned char * buf, size_t count); }; /* * @struct sm_module_operations * API to use external SM modules: * - '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, ...); * - 'module cleanup' - free resources allocated by external module. */ struct sm_module_operations { int (*initialize)(struct sc_context *ctx, struct sm_info *info, struct sc_remote_data *out); int (*get_apdus)(struct sc_context *ctx, struct sm_info *sm_info, unsigned char *init_data, size_t init_len, struct sc_remote_data *out); int (*finalize)(struct sc_context *ctx, struct sm_info *info, struct sc_remote_data *rdata, unsigned char *out, size_t out_len); int (*module_init)(struct sc_context *ctx, const char *data); int (*module_cleanup)(struct sc_context *ctx); int (*test)(struct sc_context *ctx, struct sm_info *info, char *out); }; typedef struct sm_module { char filename[128]; void *handle; struct sm_module_operations ops; } sm_module_t; /* @struct sm_context * SM context -- top level of the SM data type * - SM mode ('ACL' or 'APDU TRANSMIT'), flags; * - working SM data; * - card operations related to SM in 'APDU TRANSMIT' mode; * - external SM module; * - 'lock'/'unlock' handlers to allow SM transfer in the locked card session. */ typedef struct sm_context { char config_section[64]; unsigned sm_mode, sm_flags; struct sm_info info; struct sm_card_operations ops; struct sm_module module; unsigned long (*app_lock)(void); void (*app_unlock)(void); } sm_context_t; int sc_sm_parse_answer(struct sc_card *, unsigned char *, size_t, struct sm_card_response *); int sc_sm_update_apdu_response(struct sc_card *, unsigned char *, size_t, int, struct sc_apdu *); int sc_sm_single_transmit(struct sc_card *, struct sc_apdu *); /** * @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 card * * @return \c SC_SUCCESS or error code if an error occurred */ int sc_sm_stop(struct sc_card *card); #ifdef __cplusplus } #endif #endif OpenSC-0.26.1/src/libopensc/types.h000066400000000000000000000321701474147347300170320ustar00rootroot00000000000000/* * types.h: OpenSC general types * * Copyright (C) 2001, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _OPENSC_TYPES_H #define _OPENSC_TYPES_H #ifdef __cplusplus extern "C" { #endif typedef unsigned char u8; /* various maximum values */ #define SC_MAX_CARD_DRIVERS 48 #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 #define SC_MAX_AID_SIZE 16 #define SC_MAX_AID_STRING_SIZE (SC_MAX_AID_SIZE * 2 + 3) #define SC_MAX_IIN_SIZE 10 #define SC_MAX_OBJECT_ID_OCTETS 16 #define SC_MAX_PATH_SIZE 16 #define SC_MAX_PATH_STRING_SIZE (SC_MAX_PATH_SIZE * 2 + 3) #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, * 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 16 struct sc_lv_data { unsigned char *value; size_t len; }; struct sc_tlv_data { unsigned tag; unsigned char *value; size_t len; }; struct sc_object_id { int value[SC_MAX_OBJECT_ID_OCTETS]; }; struct sc_aid { unsigned char value[SC_MAX_AID_SIZE]; size_t len; }; struct sc_atr { unsigned char value[SC_MAX_ATR_SIZE]; size_t len; }; struct sc_uid { unsigned char value[SC_MAX_UID_SIZE]; size_t len; }; /* Issuer ID */ struct sc_iid { unsigned char value[SC_MAX_IIN_SIZE]; size_t len; }; struct sc_version { unsigned char hw_major; unsigned char hw_minor; unsigned char fw_major; unsigned char fw_minor; }; /* Discretionary ASN.1 data object */ struct sc_ddo { struct sc_aid aid; struct sc_iid iid; struct sc_object_id oid; size_t len; unsigned char *value; }; #define SC_PATH_TYPE_FILE_ID 0 #define SC_PATH_TYPE_DF_NAME 1 #define SC_PATH_TYPE_PATH 2 /* path of a file containing EnvelopedData objects */ #define SC_PATH_TYPE_PATH_PROT 3 #define SC_PATH_TYPE_FROM_CURRENT 4 #define SC_PATH_TYPE_PARENT 5 typedef struct sc_path { u8 value[SC_MAX_PATH_SIZE]; size_t len; /* The next two fields are used in PKCS15, where * a Path object can reference a portion of a file - * count octets starting at offset index. */ int index; int count; int type; struct sc_aid aid; } sc_path_t; /* Control reference template */ struct sc_crt { unsigned tag; unsigned usage; /* Usage Qualifier Byte */ unsigned algo; /* Algorithm ID */ unsigned refs[8]; /* Security Object References */ }; /* Access Control flags */ #define SC_AC_NONE 0x00000000 #define SC_AC_CHV 0x00000001 /* Card Holder Verif. */ #define SC_AC_TERM 0x00000002 /* Terminal auth. */ #define SC_AC_PRO 0x00000004 /* Secure Messaging */ #define SC_AC_AUT 0x00000008 /* Key auth. */ #define SC_AC_SYMBOLIC 0x00000010 /* internal use only */ #define SC_AC_SEN 0x00000020 /* Security Environment. */ #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 /* Operations relating to access control */ #define SC_AC_OP_SELECT 0 #define SC_AC_OP_LOCK 1 #define SC_AC_OP_DELETE 2 #define SC_AC_OP_CREATE 3 #define SC_AC_OP_REHABILITATE 4 #define SC_AC_OP_INVALIDATE 5 #define SC_AC_OP_LIST_FILES 6 #define SC_AC_OP_CRYPTO 7 #define SC_AC_OP_DELETE_SELF 8 #define SC_AC_OP_PSO_DECRYPT 9 #define SC_AC_OP_PSO_ENCRYPT 10 #define SC_AC_OP_PSO_COMPUTE_SIGNATURE 11 #define SC_AC_OP_PSO_VERIFY_SIGNATURE 12 #define SC_AC_OP_PSO_COMPUTE_CHECKSUM 13 #define SC_AC_OP_PSO_VERIFY_CHECKSUM 14 #define SC_AC_OP_INTERNAL_AUTHENTICATE 15 #define SC_AC_OP_EXTERNAL_AUTHENTICATE 16 #define SC_AC_OP_PIN_DEFINE 17 #define SC_AC_OP_PIN_CHANGE 18 #define SC_AC_OP_PIN_RESET 19 #define SC_AC_OP_ACTIVATE 20 #define SC_AC_OP_DEACTIVATE 21 #define SC_AC_OP_READ 22 #define SC_AC_OP_UPDATE 23 #define SC_AC_OP_WRITE 24 #define SC_AC_OP_RESIZE 25 #define SC_AC_OP_GENERATE 26 #define SC_AC_OP_CREATE_EF 27 #define SC_AC_OP_CREATE_DF 28 #define SC_AC_OP_ADMIN 29 #define SC_AC_OP_PIN_USE 30 /* If you add more OPs here, make sure you increase SC_MAX_AC_OPS*/ #define SC_MAX_AC_OPS 31 /* the use of SC_AC_OP_ERASE is deprecated, SC_AC_OP_DELETE should be used * instead */ #define SC_AC_OP_ERASE SC_AC_OP_DELETE #define SC_AC_KEY_REF_NONE 0xFFFFFFFF 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_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 #define SC_FILE_TYPE_BSO 0x10 /* EF structures */ #define SC_FILE_EF_UNKNOWN 0x00 #define SC_FILE_EF_TRANSPARENT 0x01 #define SC_FILE_EF_LINEAR_FIXED 0x02 #define SC_FILE_EF_LINEAR_FIXED_TLV 0x03 #define SC_FILE_EF_LINEAR_VARIABLE 0x04 #define SC_FILE_EF_LINEAR_VARIABLE_TLV 0x05 #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 */ /* 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 */ size_t namelen; /* length of DF name */ unsigned int type, ef_structure, status; /* See constant values defined above */ unsigned int shareable; /* true(1), false(0) according to ISO 7816-4:2005 Table 14 */ size_t size; /* Size of file (in bytes) */ 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 */ 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; unsigned char *prop_attr; /* proprietary information. tag '85'*/ size_t prop_attr_len; unsigned char *type_attr; /* file descriptor data. tag '82'. replaces the file's type information (DF, EF, ...) */ size_t type_attr_len; unsigned char *encoded_content; /* file's content encoded to be used in the file creation command */ size_t encoded_content_len; /* size of file's encoded content in bytes */ unsigned int magic; } sc_file_t; /* Different APDU cases */ #define SC_APDU_CASE_NONE 0x00 #define SC_APDU_CASE_1 0x01 #define SC_APDU_CASE_2_SHORT 0x02 #define SC_APDU_CASE_3_SHORT 0x03 #define SC_APDU_CASE_4_SHORT 0x04 #define SC_APDU_SHORT_MASK 0x0f #define SC_APDU_EXT 0x10 #define SC_APDU_CASE_2_EXT SC_APDU_CASE_2_SHORT | SC_APDU_EXT #define SC_APDU_CASE_3_EXT SC_APDU_CASE_3_SHORT | SC_APDU_EXT #define SC_APDU_CASE_4_EXT SC_APDU_CASE_4_SHORT | SC_APDU_EXT /* following types let OpenSC decides whether to use short or extended APDUs */ #define SC_APDU_CASE_2 0x22 #define SC_APDU_CASE_3 0x23 #define SC_APDU_CASE_4 0x24 /* use command chaining if the Lc value is greater than normally allowed */ #define SC_APDU_FLAGS_CHAINING 0x00000001UL /* do not automatically call GET RESPONSE to read all available data */ #define SC_APDU_FLAGS_NO_GET_RESP 0x00000002UL /* do not automatically try a re-transmit with a new length if the card * returns 0x6Cxx (wrong length) */ #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 #define SC_APDU_ALLOCATE_FLAG_RESP 0x04 typedef struct sc_apdu { int cse; /* APDU case */ unsigned char cla, ins, p1, p2; /* CLA, INS, P1 and P2 bytes */ size_t lc, le; /* Lc and Le bytes */ const unsigned char *data; /* S-APDU data */ size_t datalen; /* length of data in S-APDU */ unsigned char *resp; /* R-APDU data buffer */ size_t resplen; /* in: size of R-APDU buffer, * out: length of data returned in R-APDU */ unsigned char control; /* Set if APDU should go to the reader */ unsigned allocation_flags; /* APDU allocation flags */ unsigned int sw1, sw2; /* Status words returned in R-APDU */ unsigned char mac[8]; size_t mac_len; unsigned long flags; struct sc_apdu *next; } sc_apdu_t; /* Card manager Production Life Cycle data (CPLC) * (from the Open Platform specification) */ #define SC_CPLC_TAG 0x9F7F #define SC_CPLC_DER_SIZE 45 struct sc_cplc { unsigned char ic_fabricator[2]; unsigned char ic_type[2]; unsigned char os_data[6]; unsigned char ic_date[2]; unsigned char ic_serial[4]; unsigned char ic_batch_id[2]; unsigned char ic_module_data[4]; unsigned char icc_manufacturer[2]; unsigned char ic_embed_date[2]; unsigned char pre_perso_data[6]; unsigned char personalizer_data[6]; unsigned char value[SC_CPLC_DER_SIZE]; size_t len; }; /* 'Issuer Identification Number' is a part of ISO/IEC 7812 PAN definition */ struct sc_iin { unsigned char mii; /* industry identifier */ unsigned country; /* country identifier */ unsigned long issuer_id; /* issuer identifier */ }; /* structure for the card serial number (normally the ICCSN) */ #define SC_MAX_SERIALNR 32 typedef struct sc_serial_number { unsigned char value[SC_MAX_SERIALNR]; size_t len; struct sc_iin iin; } sc_serial_number_t; /** * @struct sc_remote_apdu data * Structure to supply the linked APDU data used in * communication with the external (SM) modules. */ #define SC_REMOTE_APDU_FLAG_NOT_FATAL 0x01 #define SC_REMOTE_APDU_FLAG_RETURN_ANSWER 0x02 struct sc_remote_apdu { unsigned char sbuf[2*SC_MAX_APDU_BUFFER_SIZE]; unsigned char rbuf[2*SC_MAX_APDU_BUFFER_SIZE]; struct sc_apdu apdu; unsigned flags; struct sc_remote_apdu *next; }; /** * @struct sc_remote_data * Frame for the list of the @c sc_remote_apdu data with * the handlers to allocate and free. */ struct sc_remote_data { struct sc_remote_apdu *data; int length; /** * Handler to allocate a new @c sc_remote_apdu data and add it to the list. * @param rdata Self pointer to the @c sc_remote_data * @param out Pointer to newle allocated member */ int (*alloc)(struct sc_remote_data *rdata, struct sc_remote_apdu **out); /** * Handler to free the list of @c sc_remote_apdu data * @param rdata Self pointer to the @c sc_remote_data */ void (*free)(struct sc_remote_data *rdata); }; #ifdef __cplusplus } #endif #endif OpenSC-0.26.1/src/minidriver/000077500000000000000000000000001474147347300157045ustar00rootroot00000000000000OpenSC-0.26.1/src/minidriver/Makefile.am000066400000000000000000000022441474147347300177420ustar00rootroot00000000000000include $(top_srcdir)/win32/ltrc.inc MAINTAINERCLEANFILES = $(srcdir)/Makefile.in $(srcdir)/versioninfo-minidriver.rc EXTRA_DIST = Makefile.mak versioninfo-minidriver.rc.in opensc-minidriver.dll.manifest 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 else dist_noinst_DATA = opensc-minidriver.inf endif 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_LIBADD = \ $(top_builddir)/src/libopensc/libopensc_static.la \ $(OPTIONAL_OPENSSL_LIBS) \ -lbcrypt -lcrypt32 -lrpcrt4 -lwinmm opensc_minidriver@LIBRARY_BITNESS@_la_LDFLAGS = $(AM_LDFLAGS) \ -export-symbols "$(srcdir)/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 OpenSC-0.26.1/src/minidriver/Makefile.mak000066400000000000000000000017421474147347300201170ustar00rootroot00000000000000TOPDIR = ..\.. TARGET = opensc-minidriver.dll OBJECTS = minidriver.obj versioninfo-minidriver.res LIBS = $(TOPDIR)\src\libopensc\opensc_a.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 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 if EXIST $(TARGET).manifest mt -manifest $(TARGET).manifest -outputresource:$(TARGET);2 OpenSC-0.26.1/src/minidriver/cardmod-mingw-compat.h000066400000000000000000000025651474147347300220760ustar00rootroot00000000000000/* * cardmod-mingw-compat.h: Compat defines to make minidriver with cardmod.h * buildable under mingw * * 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 */ #define __deref #define __deref_opt_inout_bcount_part_opt(x,y) #define __deref_opt_out_bcount(x) #define __deref_out_bcount(x) #define __deref_out_bcount_opt(x) #define __in #define __in_bcount_opt(x) #define __in_opt #define __inout #define __inout_bcount_opt(x) #define __out #define __out_bcount_part_opt(x,y) #define __out_bcount_part_opt(x,y) #define __out_opt #define __struct_bcount(x) #define __success(x) #define _Printf_format_string_ #ifndef NTE_BUFFER_TOO_SMALL #define NTE_BUFFER_TOO_SMALL _HRESULT_TYPEDEF_(0x80090028) #endif OpenSC-0.26.1/src/minidriver/minidriver.c000066400000000000000000006760331474147347300202370ustar00rootroot00000000000000/* * minidriver.c: OpenSC minidriver * * Copyright (C) 2009,2010 francois.leblanc@cev-sa.com * Copyright (C) 2015 vincent.letoux@mysmartlogon.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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* * This module requires "cardmod.h" from CNG SDK or platform SDK to build. */ #include "config.h" #ifdef ENABLE_MINIDRIVER #ifdef _MANAGED #pragma managed(push, off) #endif #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" #include "libopensc/pkcs15.h" #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 #include #include #endif #ifdef ENABLE_OPENPACE #include #endif #if defined(__MINGW32__) #include "cardmod-mingw-compat.h" #endif #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; #define MD_MINIMUM_VERSION_SUPPORTED 4 #define MD_CURRENT_VERSION_SUPPORTED 7 #define NULLSTR(a) (a == NULL ? "" : a) #define NULLWSTR(a) (a == NULL ? L"" : a) #define MD_MAX_KEY_CONTAINERS 32 #define MD_CARDID_SIZE 16 #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_CARDCF_LENGTH (sizeof(CARD_CACHE_FILE_FORMAT)) #define MD_KEY_USAGE_KEYEXCHANGE \ SC_PKCS15INIT_X509_KEY_ENCIPHERMENT | \ SC_PKCS15INIT_X509_DATA_ENCIPHERMENT | \ SC_PKCS15INIT_X509_DIGITAL_SIGNATURE #define MD_KEY_USAGE_KEYEXCHANGE_ECC \ SC_PKCS15INIT_X509_KEY_AGREEMENT| \ SC_PKCS15INIT_X509_DIGITAL_SIGNATURE #define MD_KEY_USAGE_SIGNATURE \ SC_PKCS15INIT_X509_DIGITAL_SIGNATURE | \ SC_PKCS15INIT_X509_KEY_CERT_SIGN | \ SC_PKCS15INIT_X509_CRL_SIGN #define MD_KEY_ACCESS \ SC_PKCS15_PRKEY_ACCESS_SENSITIVE | \ SC_PKCS15_PRKEY_ACCESS_ALWAYSSENSITIVE | \ SC_PKCS15_PRKEY_ACCESS_NEVEREXTRACTABLE | \ SC_PKCS15_PRKEY_ACCESS_LOCAL /* copied from pkcs15-cardos.c */ #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) #define USAGE_ANY_AGREEMENT (SC_PKCS15_PRKEY_USAGE_DERIVE) /* if use of internal-winscard.h */ #ifndef SCARD_E_INVALID_PARAMETER #define SCARD_E_INVALID_PARAMETER 0x80100004L #define SCARD_E_UNSUPPORTED_FEATURE 0x80100022L #define SCARD_E_NO_MEMORY 0x80100006L #define SCARD_W_WRONG_CHV 0x8010006BL #define SCARD_E_FILE_NOT_FOUND 0x80100024L #define SCARD_E_UNKNOWN_CARD 0x8010000DL #define SCARD_F_UNKNOWN_ERROR 0x80100014L #endif /* defined twice: in versioninfo-minidriver.rc.in and in minidriver.c */ #define IDI_SMARTCARD 102 #define SUBKEY_ENABLE_CANCEL "Software\\OpenSC Project\\OpenSC\\md_pinpad_dlg_enable_cancel" /* magic to determine previous pinpad authentication */ #define MAGIC_SESSION_PIN "opensc-minidriver" #define TLS1_0_PROTOCOL_VERSION 0x0301 #define TLS1_1_PROTOCOL_VERSION 0x0302 #define TLS1_2_PROTOCOL_VERSION 0x0303 #define TLS_DERIVE_KEY_SIZE 48 struct md_directory { unsigned char name[9]; CARD_DIRECTORY_ACCESS_CONDITION acl; struct md_file *files; struct md_directory *subdirs; struct md_directory *next; }; struct md_file { unsigned char name[9]; CARD_FILE_ACCESS_CONDITION acl; unsigned char *blob; size_t size; struct md_file *next; }; struct md_pkcs15_container { int index; struct sc_pkcs15_id id; char guid[MAX_CONTAINER_NAME_LEN + 1]; unsigned char flags; size_t size_key_exchange, size_sign; struct sc_pkcs15_object *cert_obj, *prkey_obj, *pubkey_obj; // BOOL guid_overwrite; }; struct md_dh_agreement { DWORD dwSize; PBYTE pbAgreement; }; struct md_guid_conversion { CHAR szOpenSCGuid[MAX_CONTAINER_NAME_LEN+1]; CHAR szWindowsGuid[MAX_CONTAINER_NAME_LEN+1]; }; #define MD_MAX_CONVERSIONS 50 struct md_guid_conversion md_static_conversions[MD_MAX_CONVERSIONS] = {0}; typedef struct _VENDOR_SPECIFIC { BOOL initialized; struct sc_pkcs15_object *pin_objs[MD_MAX_PINS]; struct sc_context *ctx; struct sc_reader *reader; struct sc_card *card; struct sc_pkcs15_card *p15card; struct md_pkcs15_container p15_containers[MD_MAX_KEY_CONTAINERS]; struct md_directory root; SCARDCONTEXT hSCardCtx; SCARDHANDLE hScard; /* These will be used in CardAuthenticateEx to display a dialog box when doing * external PIN verification. */ HWND hwndParent; LPWSTR wszPinContext; /* these will be used to store intermediate dh agreements results */ struct md_dh_agreement* dh_agreements; BYTE allocatedAgreements; /* if any key used with the MD_ROLE_USER_SIGN has user_consent set PinCacheAlwaysPrompt */ int need_pin_always; CRITICAL_SECTION hScard_lock; } VENDOR_SPECIFIC; static DWORD md_translate_OpenSC_to_Windows_error(int OpenSCerror, DWORD dwDefaulCode); 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); static void md_fs_finalize(PCARD_DATA pCardData); #if defined(__GNUC__) static void logprintf(PCARD_DATA pCardData, int level, const char* format, ...) __attribute__ ((format (SC_PRINTF_FORMAT, 3, 4))); #endif static void logprintf(PCARD_DATA pCardData, int level, _Printf_format_string_ const char* format, ...) { va_list arg; VENDOR_SPECIFIC *vs; /* 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 any crash * close so as the file is not left open during any wait. */ DWORD md_debug = 0; size_t sz = sizeof(md_debug); int rv; rv = sc_ctx_win32_get_config_value("CARDMOD_LOW_LEVEL_DEBUG", "MiniDriverDebug", "Software\\OpenSC Project\\OpenSC", (char *)(&md_debug), &sz); if (rv == SC_SUCCESS && md_debug != 0) { FILE *lldebugfp = fopen("C:\\tmp\\md.log","a+"); if (lldebugfp) { va_start(arg, format); vfprintf(lldebugfp, format, arg); va_end(arg); fflush(lldebugfp); fclose(lldebugfp); } } va_start(arg, format); if(pCardData != NULL) { vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); if(vs != NULL && vs->ctx != NULL) sc_do_log_noframe(vs->ctx, level, format, arg); } va_end(arg); } static void loghex(PCARD_DATA pCardData, int level, PBYTE data, size_t len) { char line[74]; char *c; unsigned int i, a; unsigned char * p; logprintf(pCardData, level, "--- %p:%"SC_FORMAT_LEN_SIZE_T"u\n", data, len); if (data == NULL || len <= 0) return; p = data; c = line; i = 0; a = 0; memset(line, 0, sizeof(line)); while(i < len) { snprintf(c, sizeof(line) - (size_t)(c - line), "%02X", *p); line[sizeof(line) - 1] = 0; p++; c += 2; i++; if (i%32 == 0) { logprintf(pCardData, level, " %04X %s\n", a, line); a +=32; memset(line, 0, sizeof(line)); c = line; } else { if (i%4 == 0) *(c++) = ' '; if (i%16 == 0) *(c++) = ' '; } } if (i%32 != 0) 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 is OK, has been removed, or the * caller has changed the handles. * if so, then try to reinit card * or if different handles but same reader, just use the handles */ static DWORD check_card_reader_status(PCARD_DATA pCardData, const char *name) { VENDOR_SPECIFIC *vs = NULL; DWORD dwRet; int r; logprintf(pCardData, 1, "check_reader_status for %s\n", name); if(!pCardData) 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) MD_FUNC_RETURN(pCardData, 3, SCARD_E_INVALID_PARAMETER); 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 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); } 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; MD_FUNC_CALLED(pCardData, 1); if (!pCardData) MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); if (!ret_obj) 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, &idx, ret_obj); } } } else if (role == ROLE_ADMIN) { /* Get SO PIN; if no, get the 'global' PUK; if no get the 'local' one */ 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 %u\n", (unsigned int)role); MD_FUNC_RETURN(pCardData, 1, 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; 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; HICON 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) { 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; 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_bool(atrblock, flag_name, ret_default) ? TRUE : FALSE; } 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"); 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); } /* 'Write' mode can be enabled from the OpenSC configuration file*/ static BOOL 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", defaultvalue); } /* Get know if the GUID has to used as ID of crypto objects */ 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", FALSE); } /* Get know if the GUID has to used as label of crypto objects */ 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", FALSE); } /* Get know if disabled CARD_CREATE_CONTAINER_KEY_GEN mechanism */ 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", TRUE); } /* Get know if disabled CARD_CREATE_CONTAINER_KEY_IMPORT mechanism */ 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", TRUE); } /* generate unique key label (GUID)*/ static VOID md_generate_guid( __in_ecount(MAX_CONTAINER_NAME_LEN+1) PSTR szGuid) { RPC_CSTR szRPCGuid = NULL; GUID Label = {0}; UuidCreate(&Label); if (UuidToStringA(&Label, &szRPCGuid) == RPC_S_OK && szRPCGuid) { strlcpy(szGuid, (PSTR)szRPCGuid, MAX_CONTAINER_NAME_LEN + 1); RpcStringFreeA(&szRPCGuid); } else szGuid[0] = 0; } static DWORD md_contguid_get_guid_from_card(PCARD_DATA pCardData, struct sc_pkcs15_object *prkey, __in_ecount(MAX_CONTAINER_NAME_LEN+1) PSTR szGuid) { int rv; VENDOR_SPECIFIC *vs; 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); return SCARD_F_INTERNAL_ERROR; } return SCARD_S_SUCCESS; } /* add a new entry in the guid conversion table */ static DWORD md_contguid_add_conversion(PCARD_DATA pCardData, struct sc_pkcs15_object *prkey, __in_ecount(MAX_CONTAINER_NAME_LEN+1) PSTR szWindowsGuid) { DWORD ret; int i; CHAR szOpenSCGuid[MAX_CONTAINER_NAME_LEN+1] = ""; ret = md_contguid_get_guid_from_card(pCardData, prkey, szOpenSCGuid); if (ret != SCARD_S_SUCCESS) return ret; if (strcmp(szOpenSCGuid, szWindowsGuid) == 0) return ret; for (i = 0; i < MD_MAX_CONVERSIONS; i++) { if (md_static_conversions[i].szWindowsGuid[0] == 0) { strlcpy(md_static_conversions[i].szWindowsGuid, szWindowsGuid, MAX_CONTAINER_NAME_LEN + 1); 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;; } } logprintf(pCardData, 0, "md_contguid_add_conversion(): Unable to add a new conversion with guid %s.\n", szWindowsGuid); return SCARD_F_INTERNAL_ERROR;; } /* remove an entry in the guid conversion table*/ static VOID md_contguid_delete_conversion(PCARD_DATA pCardData, __in_ecount(MAX_CONTAINER_NAME_LEN+1) PSTR szWindowsGuid) { int i; for (i = 0; i < MD_MAX_CONVERSIONS; i++) { if (strcmp(md_static_conversions[i].szWindowsGuid,szWindowsGuid) == 0) { memset(md_static_conversions + i, 0, sizeof(struct md_guid_conversion)); } } } /* build key args from the minidriver guid */ static VOID md_contguid_build_key_args_from_cont_guid(PCARD_DATA pCardData, __in_ecount(MAX_CONTAINER_NAME_LEN+1) PSTR szGuid, struct sc_pkcs15init_prkeyargs *prkey_args) { /* strlen(szGuid) <= MAX_CONTAINER_NAME */ logprintf(pCardData, 3, "Using the guid '%s'\n", szGuid); if (szGuid[0] != 0) { prkey_args->guid = (unsigned char*) szGuid; prkey_args->guid_len = strlen(szGuid); } if (md_is_guid_as_id(pCardData)) { memcpy(prkey_args->id.value, szGuid, strlen(szGuid)); prkey_args->id.len = strlen(szGuid); } if (md_is_guid_as_label(pCardData)) { prkey_args->label = szGuid; } } /* build minidriver guid from the key */ static DWORD md_contguid_build_cont_guid_from_key(PCARD_DATA pCardData, struct sc_pkcs15_object *key_obj, __in_ecount(MAX_CONTAINER_NAME_LEN+1) PSTR szGuid) { struct sc_pkcs15_prkey_info *prkey_info = (struct sc_pkcs15_prkey_info *)key_obj->data; DWORD dwret = SCARD_S_SUCCESS; szGuid[0] = '\0'; /* 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; } else if (md_is_guid_as_label(pCardData) && key_obj->label[0] != 0) { strlcpy(szGuid, key_obj->label, MAX_CONTAINER_NAME_LEN + 1); } else { dwret = md_contguid_get_guid_from_card(pCardData, key_obj, szGuid); } return dwret; } static DWORD md_cont_flags_from_key(PCARD_DATA pCardData, struct sc_pkcs15_object *key_obj, unsigned char *cont_flags) { struct sc_pkcs15_prkey_info *prkey_info = NULL; VENDOR_SPECIFIC *vs; 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; if (prkey_info->aux_data) { rv = sc_aux_data_get_md_flags(vs->ctx, prkey_info->aux_data, cont_flags); if (rv != SC_ERROR_NOT_SUPPORTED && rv != SC_SUCCESS) return SCARD_F_INTERNAL_ERROR; } return SCARD_S_SUCCESS; } /* Search directory by name and optionally by name of it's parent */ static DWORD md_fs_find_directory(PCARD_DATA pCardData, struct md_directory *parent, char *name, struct md_directory **out) { VENDOR_SPECIFIC *vs; struct md_directory *dir = NULL; if (out) *out = NULL; if (!pCardData) return SCARD_E_INVALID_PARAMETER; vs = pCardData->pvVendorSpecific; if (!vs) return SCARD_E_INVALID_PARAMETER; if (!parent) parent = &vs->root; if (!name) { dir = parent; } else { dir = parent->subdirs; while(dir) { if (strlen(name) > sizeof dir->name || !strncmp((char *)dir->name, name, sizeof dir->name)) break; dir = dir->next; } } if (!dir) return SCARD_E_DIR_NOT_FOUND; if (out) *out = dir; logprintf(pCardData, 3, "MD virtual file system: found '%s' directory\n", name); return SCARD_S_SUCCESS; } static DWORD md_fs_add_directory(PCARD_DATA pCardData, struct md_directory **head, char *name, CARD_DIRECTORY_ACCESS_CONDITION acl, struct md_directory **out) { struct md_directory *new_dir = NULL; if (!pCardData || !head || !name) return SCARD_E_INVALID_PARAMETER; new_dir = pCardData->pfnCspAlloc(sizeof(struct md_directory)); if (!new_dir) return SCARD_E_NO_MEMORY; memset(new_dir, 0, sizeof(struct md_directory)); strlcpy((char *)new_dir->name, name, sizeof(new_dir->name)); new_dir->acl = acl; if (*head == NULL) { *head = new_dir; } else { struct md_directory *last = *head; while (last->next) last = last->next; last->next = new_dir; } if (out) *out = new_dir; logprintf(pCardData, 3, "MD virtual file system: directory '%s' added\n", name); return SCARD_S_SUCCESS; } static DWORD md_fs_find_file(PCARD_DATA pCardData, char *parent, char *name, struct md_file **out) { struct md_file *file = NULL; struct md_directory *dir = NULL; DWORD dwret; if (out) *out = NULL; if (!pCardData || !name) return SCARD_E_INVALID_PARAMETER; dwret = md_fs_find_directory(pCardData, NULL, parent, &dir); if (dwret != SCARD_S_SUCCESS) { logprintf(pCardData, 2, "find directory '%s' error: %lX\n", parent ? parent : "", (unsigned long)dwret); return dwret; } else if (!dir) { logprintf(pCardData, 2, "directory '%s' not found\n", parent ? parent : ""); return SCARD_E_INVALID_PARAMETER; } for (file = dir->files; file!=NULL;) { if (sizeof file->name < strlen(name) || !strncmp((char *)file->name, name, sizeof file->name)) break; file = file->next; } if (!file) return SCARD_E_FILE_NOT_FOUND; if (out) *out = file; logprintf(pCardData, 3, "MD virtual file system: found '%s' file\n", name); return SCARD_S_SUCCESS; } static DWORD md_fs_add_file(PCARD_DATA pCardData, struct md_file **head, char *name, CARD_FILE_ACCESS_CONDITION acl, unsigned char *blob, size_t size, struct md_file **out) { struct md_file *new_file = NULL; if (!pCardData || !head || !name) return SCARD_E_INVALID_PARAMETER; new_file = pCardData->pfnCspAlloc(sizeof(struct md_file)); if (!new_file) return SCARD_E_NO_MEMORY; memset(new_file, 0, sizeof(struct md_file)); strlcpy((char *)new_file->name, name, sizeof(new_file->name)); new_file->size = size; new_file->acl = acl; if (size) { new_file->blob = pCardData->pfnCspAlloc(size); if (!new_file->blob) { pCardData->pfnCspFree(new_file); return SCARD_E_NO_MEMORY; } if (blob) CopyMemory(new_file->blob, blob, size); else memset(new_file->blob, 0, size); } if (*head == NULL) { *head = new_file; } else { struct md_file *last = *head; while (last->next) last = last->next; last->next = new_file; } if (out) *out = new_file; logprintf(pCardData, 3, "MD virtual file system: file '%s' added\n", name); return SCARD_S_SUCCESS; } static void md_fs_free_file(PCARD_DATA pCardData, struct md_file *file) { if (!file) return; if (file->blob) pCardData->pfnCspFree(file->blob); file->blob = NULL; file->size = 0; pCardData->pfnCspFree(file); } static DWORD md_fs_delete_file(PCARD_DATA pCardData, char *parent, char *name) { VENDOR_SPECIFIC *vs; struct md_file *file = NULL, *file_to_rm = NULL; struct md_directory *dir = NULL; int deleted = 0; DWORD dwret; if (!pCardData || !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) { logprintf(pCardData, 2, "find directory '%s' error: %lX\n", parent ? parent : "", (unsigned long)dwret); return dwret; } else if (!dir) { logprintf(pCardData, 2, "directory '%s' not found\n", parent ? parent : ""); return SCARD_E_INVALID_PARAMETER; } else if (!dir->files) { logprintf(pCardData, 2, "no files in '%s' directory\n", parent ? parent : ""); return SCARD_E_FILE_NOT_FOUND; } if (sizeof dir->files->name < strlen(name) || !strncmp((char *)dir->files->name, name, sizeof dir->files->name)) { file_to_rm = dir->files; dir->files = dir->files->next; md_fs_free_file(pCardData, file_to_rm); dwret = SCARD_S_SUCCESS; } else { for (file = dir->files; file!=NULL; file = file->next) { if (!file->next) break; if (sizeof file->next->name < strlen(name) || !strncmp((char *)file->next->name, name, sizeof file->next->name)) { file_to_rm = file->next; file->next = file->next->next; md_fs_free_file(pCardData, file_to_rm); deleted = 1; break; } } dwret = deleted ? SCARD_S_SUCCESS : SCARD_E_FILE_NOT_FOUND; } if (!strcmp(parent, "mscp")) { int idx = -1; if(sscanf(name, "ksc%d", &idx) > 0) { } else if(sscanf(name, "kxc%d", &idx) > 0) { } if (idx >= 0 && idx < MD_MAX_KEY_CONTAINERS) { dwret = md_pkcs15_delete_object(pCardData, vs->p15_containers[idx].cert_obj); vs->p15_containers[idx].cert_obj = NULL; if(dwret != SCARD_S_SUCCESS) logprintf(pCardData, 2, "Cannot delete certificate PKCS#15 object #%i: dwret 0x%lX\n", idx, (unsigned long)dwret); } } return dwret; } static void md_fs_finalize(PCARD_DATA pCardData) { VENDOR_SPECIFIC *vs; struct md_file *file = NULL, *file_to_rm; struct md_directory *dir = NULL, *dir_to_rm; if (!pCardData) return; vs = pCardData->pvVendorSpecific; if (!vs) return; file = vs->root.files; while (file != NULL) { file_to_rm = file; file = file->next; md_fs_free_file(pCardData, file_to_rm); } vs->root.files = NULL; dir = vs->root.subdirs; while(dir) { file = dir->files; while (file != NULL) { file_to_rm = file; file = file->next; md_fs_free_file(pCardData, file_to_rm); } dir_to_rm = dir; dir = dir->next; pCardData->pfnCspFree(dir_to_rm); } vs->root.subdirs = NULL; } /* * Update 'soft' containers. * Called each time when 'WriteFile' is called for 'cmapfile'. */ static DWORD md_pkcs15_update_containers(PCARD_DATA pCardData, unsigned char *blob, size_t size) { VENDOR_SPECIFIC *vs; CONTAINER_MAP_RECORD *pp; int nn_records, idx; if (!pCardData || !blob || size < sizeof(CONTAINER_MAP_RECORD)) 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) nn_records = MD_MAX_KEY_CONTAINERS; for (idx=0, pp = (CONTAINER_MAP_RECORD *)blob; idxp15_containers[idx]); size_t count; CHAR szGuid[MAX_CONTAINER_NAME_LEN+1] = ""; count = wcstombs(szGuid, pp->wszGuid, sizeof(cont->guid)); if (!count) { if (cont->guid[0] != 0) { md_contguid_delete_conversion(pCardData, cont->guid); } memset(cont, 0, sizeof(CONTAINER_MAP_RECORD)); } else { strlcpy(cont->guid, szGuid, MAX_CONTAINER_NAME_LEN + 1); cont->index = idx; cont->flags = pp->bFlags; cont->size_sign = pp->wSigKeySizeBits; cont->size_key_exchange = pp->wKeyExchangeKeySizeBits; logprintf(pCardData, 3, "update P15 containers: touch container (idx:%i,id:%s,guid:%.*s,flags:%X)\n", idx, sc_pkcs15_print_id(&cont->id), (int)sizeof cont->guid, cont->guid, cont->flags); } } return SCARD_S_SUCCESS; } static DWORD md_pkcs15_delete_object(PCARD_DATA pCardData, struct sc_pkcs15_object *obj) { VENDOR_SPECIFIC *vs; struct sc_profile *profile = NULL; struct sc_card *card = NULL; struct sc_app_info *app_info = NULL; DWORD dwret = SCARD_F_INTERNAL_ERROR; int rv; if (!pCardData) return SCARD_E_INVALID_PARAMETER; vs = pCardData->pvVendorSpecific; if (!vs) return SCARD_E_INVALID_PARAMETER; card = vs->p15card->card; if (!obj) return SCARD_S_SUCCESS; logprintf(pCardData, 3, "MdDeleteObject('%.*s',type:0x%X) called\n", (int) sizeof obj->label, obj->label, obj->type); rv = sc_lock(card); if (rv) { logprintf(pCardData, 3, "MdDeleteObject(): cannot lock card\n"); return SCARD_F_INTERNAL_ERROR; } app_info = vs->p15card->app; rv = sc_pkcs15init_bind(card, "pkcs15", NULL, NULL, &profile); if (rv) { logprintf(pCardData, 3, "MdDeleteObject(): PKCS#15 bind failed\n"); sc_unlock(card); return SCARD_F_INTERNAL_ERROR; } rv = sc_pkcs15init_finalize_profile(card, profile, app_info ? &app_info->aid : NULL); if (rv) { logprintf(pCardData, 3, "MdDeleteObject(): cannot finalize profile\n"); goto done; } sc_pkcs15init_set_p15card(profile, vs->p15card); rv = sc_pkcs15init_delete_object(vs->p15card, profile, obj); if (rv) { logprintf(pCardData, 2, "MdDeleteObject(): pkcs15init delete object failed %d\n", rv); goto done; } dwret = SCARD_S_SUCCESS; logprintf(pCardData, 3, "MdDeleteObject() returns OK\n"); done: sc_pkcs15init_unbind(profile); sc_unlock(card); return dwret; } /* Set 'soft' file contents, * and update data associated to 'cardcf' and 'cmapfile'. */ static DWORD md_fs_set_content(PCARD_DATA pCardData, struct md_file *file, unsigned char *blob, size_t size) { if (!pCardData || !file) return SCARD_E_INVALID_PARAMETER; if (file->blob) pCardData->pfnCspFree(file->blob); file->blob = pCardData->pfnCspAlloc(size); if (!file->blob) return SCARD_E_NO_MEMORY; CopyMemory(file->blob, blob, size); file->size = size; if (!strcmp((char *)file->name, "cmapfile")) return md_pkcs15_update_containers(pCardData, blob, size); return SCARD_S_SUCCESS; } /* * Set 'cardid' from the 'serialNumber' attribute of the 'tokenInfo' */ static DWORD md_set_cardid(PCARD_DATA pCardData, struct md_file *file) { VENDOR_SPECIFIC *vs; DWORD dwret; if (!pCardData || !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]; size_t offs, wr, sn_len = sizeof(sn_bin); int rv; rv = sc_hex_to_bin(vs->p15card->tokeninfo->serial_number, sn_bin, &sn_len); if (rv) { sn_len = strlen(vs->p15card->tokeninfo->serial_number); if (sn_len > SC_MAX_SERIALNR) { sn_len = SC_MAX_SERIALNR; } memcpy(sn_bin, vs->p15card->tokeninfo->serial_number, sn_len); } 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); if (dwret != SCARD_S_SUCCESS) return dwret; } logprintf(pCardData, 3, "cardid(%"SC_FORMAT_LEN_SIZE_T"u)\n", file->size); loghex(pCardData, 3, file->blob, file->size); return SCARD_S_SUCCESS; } /* fill the msroots file from root certificates */ static DWORD md_fs_read_msroots_file(PCARD_DATA pCardData, struct md_file *file) { CERT_BLOB dbStore = {0}; HCERTSTORE hCertStore; VENDOR_SPECIFIC *vs; int rv, ii, cert_num; struct sc_pkcs15_object *prkey_objs[MD_MAX_KEY_CONTAINERS]; DWORD dwret = SCARD_F_INTERNAL_ERROR; if (!pCardData || !file) return SCARD_E_INVALID_PARAMETER; vs = (VENDOR_SPECIFIC *) pCardData->pvVendorSpecific; if (!vs) return SCARD_E_INVALID_PARAMETER; hCertStore = CertOpenStore(CERT_STORE_PROV_MEMORY, X509_ASN_ENCODING, (HCRYPTPROV_LEGACY)NULL, 0, NULL); if (!hCertStore) goto Ret; rv = sc_pkcs15_get_objects(vs->p15card, SC_PKCS15_TYPE_CERT_X509, prkey_objs, MD_MAX_KEY_CONTAINERS); if (rv < 0) { logprintf(pCardData, 0, "certificate enumeration failed: %s\n", sc_strerror(rv)); dwret = md_translate_OpenSC_to_Windows_error(rv, dwret); goto Ret; } cert_num = rv; 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) { 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; } wincert = CertCreateCertificateContext(X509_ASN_ENCODING, cert->data.value, (DWORD) cert->data.len); if (wincert) { CertAddCertificateContextToStore(hCertStore, wincert, CERT_STORE_ADD_REPLACE_EXISTING, NULL); CertFreeCertificateContext(wincert); } else { logprintf(pCardData, 2, "unable to load the certificate from Windows 0x%08X\n", (unsigned int)GetLastError()); } sc_pkcs15_free_certificate(cert); } } if (FALSE == CertSaveStore( hCertStore, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, CERT_STORE_SAVE_AS_PKCS7, CERT_STORE_SAVE_TO_MEMORY, &dbStore, 0)) { goto Ret; } dbStore.pbData = (PBYTE) pCardData->pfnCspAlloc(dbStore.cbData); if (NULL == dbStore.pbData) { dwret = SCARD_E_NO_MEMORY; goto Ret; } if (FALSE == CertSaveStore( hCertStore, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, CERT_STORE_SAVE_AS_PKCS7, CERT_STORE_SAVE_TO_MEMORY, &dbStore, 0)) { goto Ret; } file->size = dbStore.cbData; file->blob = dbStore.pbData; dbStore.pbData = NULL; dwret = SCARD_S_SUCCESS; Ret: if (dbStore.pbData) pCardData->pfnCspFree(dbStore.pbData); if (hCertStore) CertCloseStore(hCertStore, CERT_CLOSE_STORE_FORCE_FLAG); return dwret; } /* * Return content of the 'soft' file. */ static DWORD md_fs_read_content(PCARD_DATA pCardData, char *parent, struct md_file *file) { VENDOR_SPECIFIC *vs; struct md_directory *dir = NULL; DWORD dwret; if (!pCardData || !file) return SCARD_E_INVALID_PARAMETER; vs = pCardData->pvVendorSpecific; if (!vs || !vs->p15card) return SCARD_E_INVALID_PARAMETER; dwret = md_fs_find_directory(pCardData, NULL, parent, &dir); if (dwret != SCARD_S_SUCCESS) { logprintf(pCardData, 2, "find directory '%s' error: %lX\n", parent ? parent : "", (unsigned long)dwret); return dwret; } else if (!dir) { logprintf(pCardData, 2, "directory '%s' not found\n", parent ? parent : ""); return SCARD_E_DIR_NOT_FOUND; } if (!strcmp((char *)dir->name, "mscp")) { int idx, rv; if(sscanf((char *)file->name, "ksc%d", &idx) > 0) { } else if(sscanf((char *)file->name, "kxc%d", &idx) > 0) { } else { idx = -1; } if (idx >=0 && idx < MD_MAX_KEY_CONTAINERS && vs->p15_containers[idx].cert_obj) { 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, 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"); return md_translate_OpenSC_to_Windows_error(rv, SCARD_F_INTERNAL_ERROR); } file->blob = pCardData->pfnCspAlloc(cert->data.len); if (file->blob) { CopyMemory(file->blob, cert->data.value, cert->data.len); file->size = cert->data.len; dwret = SCARD_S_SUCCESS; } else dwret = SCARD_E_NO_MEMORY; sc_pkcs15_free_certificate(cert); return dwret; } else if (!strcmp((char *)file->name, "msroots")) return md_fs_read_msroots_file(pCardData, file); } return SCARD_E_FILE_NOT_FOUND; } /* * Set content of 'cardcf', * for that look for the possible source in the following order: * - data from the dedicated PKCS#15 'DATA' object; * - 'lastUpdate' attribute of tokenInfo; * - random data. */ static DWORD md_set_cardcf(PCARD_DATA pCardData, struct md_file *file) { CARD_CACHE_FILE_FORMAT empty = {0}; DWORD dwret; if (!pCardData || !file) return SCARD_E_INVALID_PARAMETER; dwret = md_fs_set_content(pCardData, file, (unsigned char *)(&empty), MD_CARDCF_LENGTH); if (dwret != SCARD_S_SUCCESS) return dwret; logprintf(pCardData, 3, "'cardcf' content(%"SC_FORMAT_LEN_SIZE_T"u)\n", file->size); loghex(pCardData, 3, file->blob, file->size); return SCARD_S_SUCCESS; } static DWORD md_set_cardapps(PCARD_DATA pCardData, struct md_file *file) { DWORD dwret; unsigned char mscp[8] = {'m','s','c','p',0,0,0,0}; if (!pCardData || !file) return SCARD_E_INVALID_PARAMETER; dwret = md_fs_set_content(pCardData, file, mscp, sizeof(mscp)); if (dwret != SCARD_S_SUCCESS) return dwret; logprintf(pCardData, 3, "mscp(%"SC_FORMAT_LEN_SIZE_T"u)\n", file->size); loghex(pCardData, 3, file->blob, file->size); return SCARD_S_SUCCESS; } /* check if the card has root certificates. If yes, notify the base csp by creating the msroots file */ static DWORD md_fs_add_msroots(PCARD_DATA pCardData, struct md_file **head) { VENDOR_SPECIFIC *vs; int rv, ii, cert_num; DWORD dwret; struct sc_pkcs15_object *prkey_objs[MD_MAX_KEY_CONTAINERS]; if (!pCardData || !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) { logprintf(pCardData, 0, "certificate enumeration failed: %s\n", sc_strerror(rv)); return SCARD_S_SUCCESS; } cert_num = rv; for(ii = 0; ii < cert_num; ii++) { struct sc_pkcs15_cert_info *cert_info = (struct sc_pkcs15_cert_info *) prkey_objs[ii]->data; if (cert_info->authority) { dwret = md_fs_add_file(pCardData, head, "msroots", EveryoneReadUserWriteAc, NULL, 0, NULL); if (dwret != SCARD_S_SUCCESS) return dwret; return SCARD_S_SUCCESS; } } return SCARD_S_SUCCESS; } /* * Set the content of the 'soft' 'cmapfile': * 1. Initialize internal p15_contaniers with the existing private keys PKCS#15 objects; * 2. Try to read the content of the PKCS#15 'DATA' object 'CSP':'cmapfile', * If some record from the 'DATA' object references an existing key: * 2a. Update the non-pkcs#15 attributes of the corresponding internal p15_container; * 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. 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; size_t cmap_len; DWORD dwret; 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; if (!vs) return SCARD_E_INVALID_PARAMETER; 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; } conts_num = rv; logprintf(pCardData, 2, "Found %d private key(s) in the card.\n", conts_num); /* Initialize the P15 container array with the existing keys */ for(ii = 0; ii < conts_num; ii++) { struct sc_pkcs15_object *key_obj = prkey_objs[ii]; struct sc_pkcs15_prkey_info *prkey_info = (struct sc_pkcs15_prkey_info *)key_obj->data; struct md_pkcs15_container *cont = &vs->p15_containers[ii]; if(key_obj->type != SC_PKCS15_TYPE_PRKEY_RSA && key_obj->type != SC_PKCS15_TYPE_PRKEY_EC) { logprintf(pCardData, 7, "Non 'RSA' 'EC' key (type:%X) are ignored\n", key_obj->type); continue; } dwret = md_contguid_build_cont_guid_from_key(pCardData, key_obj, cont->guid); if (dwret != SCARD_S_SUCCESS) return dwret; /* replace the OpenSC guid by a Windows Guid if needed Typically used in the certificate enrollment process. Windows create a new container with a Windows guid, close the context, then create a new context and look for the previous container. If we return our guid, it fails because the Windows guid can't be found. The overwrite is present to avoid this conversion been replaced by md_pkcs15_update_container_from_do*/ // cont->guid_overwrite = md_contguid_find_conversion(pCardData, cont->guid); // cont->flags = CONTAINER_MAP_VALID_CONTAINER; dwret = md_cont_flags_from_key(pCardData, key_obj, &cont->flags); if (dwret != SCARD_S_SUCCESS) return dwret; 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, * it allows 'decryption' as well as 'signature' key usage. * AT_SIGNATURE allows only 'signature' usage. */ cont->size_key_exchange = cont->size_sign = 0; if (key_obj->type == SC_PKCS15_TYPE_PRKEY_RSA) { if (prkey_info->usage & USAGE_ANY_DECIPHER) cont->size_key_exchange = prkey_info->modulus_length; else if (prkey_info->usage & USAGE_ANY_SIGN) cont->size_sign = prkey_info->modulus_length; else cont->size_key_exchange = prkey_info->modulus_length; } else if (key_obj->type == SC_PKCS15_TYPE_PRKEY_EC) { if (prkey_info->usage & USAGE_ANY_AGREEMENT) cont->size_key_exchange = prkey_info->field_length; else if (prkey_info->usage & USAGE_ANY_SIGN) cont->size_sign = prkey_info->field_length; else cont->size_key_exchange = prkey_info->field_length; } 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); cont->id = prkey_info->id; cont->prkey_obj = prkey_objs[ii]; /* 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); if (!sc_pkcs15_find_pubkey_by_id(vs->p15card, &cont->id, &cont->pubkey_obj)) logprintf(pCardData, 2, "found public key friend '%.*s'\n", (int) sizeof cont->pubkey_obj->label, cont->pubkey_obj->label); } if (conts_num) { /* Read 'CMAPFILE' (Gemalto style) and update the attributes of P15 containers */ #if 0 struct sc_pkcs15_object *dobjs[MD_MAX_KEY_CONTAINERS + 1], *default_cont = NULL; int num_dobjs = MD_MAX_KEY_CONTAINERS + 1; rv = sc_pkcs15_get_objects(vs->p15card, SC_PKCS15_TYPE_DATA_OBJECT, dobjs, num_dobjs); if (rv < 0) { logprintf(pCardData, 0, "'DATA' object enumeration failed: %s\n", sc_strerror(rv)); return SCARD_F_UNKNOWN_ERROR; } num_dobjs = rv; logprintf(pCardData, 2, "Found %d 'DATA' objects.\n", num_dobjs); for (ii=0;iidata; 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, "Default Key Container", sizeof dobjs[ii]->label)) { default_cont = dobjs[ii]; continue; } dwret = md_pkcs15_update_container_from_do(pCardData, dobjs[ii]); if (dwret != SCARD_S_SUCCESS) { logprintf(pCardData, 2, "Cannot update container from DO: %li", dwret); return dwret; } } if (default_cont) { dwret = md_pkcs15_default_container_from_do(pCardData, default_cont); if (dwret != SCARD_S_SUCCESS) { logprintf(pCardData, 2, "Cannot set default container from DO: %li", dwret); return dwret; } } #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; iip15_containers[ii].flags & CONTAINER_MAP_VALID_CONTAINER)) continue; if (!found_default) { vs->p15_containers[ii].flags |= CONTAINER_MAP_DEFAULT_CONTAINER; found_default = 1; } mbstowcs((p+ii)->wszGuid, vs->p15_containers[ii].guid, MAX_CONTAINER_NAME_LEN + 1); (p+ii)->bFlags = vs->p15_containers[ii].flags; (p+ii)->wSigKeySizeBits = (WORD) vs->p15_containers[ii].size_sign; (p+ii)->wKeyExchangeKeySizeBits = (WORD) vs->p15_containers[ii].size_key_exchange; if (vs->p15_containers[ii].cert_obj) { char k_name[6]; if (vs->p15_containers[ii].size_key_exchange) { snprintf(k_name, sizeof(k_name), "kxc%02i", ii); k_name[sizeof(k_name) - 1] = 0; dwret = md_fs_add_file(pCardData, &(file->next), k_name, file->acl, NULL, 0, NULL); if (dwret != SCARD_S_SUCCESS) return dwret; } if (vs->p15_containers[ii].size_sign) { snprintf(k_name, sizeof(k_name), "ksc%02i", ii); k_name[sizeof(k_name) - 1] = 0; dwret = md_fs_add_file(pCardData, &(file->next), k_name, file->acl, NULL, 0, NULL); if (dwret != SCARD_S_SUCCESS) return dwret; } } logprintf(pCardData, 7, "cmapfile entry(%d) '%s' ",ii, vs->p15_containers[ii].guid); loghex(pCardData, 7, (PBYTE) (p+ii), sizeof(CONTAINER_MAP_RECORD)); } } dwret = md_fs_add_msroots(pCardData, &(file->next)); if (dwret != SCARD_S_SUCCESS) return dwret; dwret = md_fs_set_content(pCardData, file, cmap_buf, cmap_len); pCardData->pfnCspFree(cmap_buf); if (dwret != SCARD_S_SUCCESS) return dwret; logprintf(pCardData, 3, "cmap(%"SC_FORMAT_LEN_SIZE_T"u)\n", file->size); loghex(pCardData, 3, file->blob, file->size); return SCARD_S_SUCCESS; } /* * Initialize internal 'soft' file system */ static DWORD md_fs_init(PCARD_DATA pCardData) { VENDOR_SPECIFIC *vs; DWORD dwret; struct md_file *cardid, *cardcf, *cardapps, *cmapfile; struct md_directory *mscp; if (!pCardData || !pCardData->pvVendorSpecific) return SCARD_E_INVALID_PARAMETER; vs = pCardData->pvVendorSpecific; dwret = md_fs_add_file(pCardData, &(vs->root.files), "cardid", EveryoneReadAdminWriteAc, NULL, 0, &cardid); if (dwret != SCARD_S_SUCCESS) return dwret; dwret = md_set_cardid(pCardData, cardid); if (dwret != SCARD_S_SUCCESS) goto ret_cleanup; dwret = md_fs_add_file(pCardData, &(vs->root.files), "cardcf", EveryoneReadUserWriteAc, NULL, 0, &cardcf); if (dwret != SCARD_S_SUCCESS) goto ret_cleanup; dwret = md_set_cardcf(pCardData, cardcf); if (dwret != SCARD_S_SUCCESS) goto ret_cleanup; dwret = md_fs_add_file(pCardData, &(vs->root.files), "cardapps", EveryoneReadAdminWriteAc, NULL, 0, &cardapps); if (dwret != SCARD_S_SUCCESS) goto ret_cleanup; dwret = md_set_cardapps(pCardData, cardapps); if (dwret != SCARD_S_SUCCESS) goto ret_cleanup; dwret = md_fs_add_directory(pCardData, &(vs->root.subdirs), "mscp", (CARD_DIRECTORY_ACCESS_CONDITION)UserCreateDeleteDirAc, &mscp); if (dwret != SCARD_S_SUCCESS) goto ret_cleanup; dwret = md_fs_add_file(pCardData, &(mscp->files), "cmapfile", EveryoneReadUserWriteAc, NULL, 0, &cmapfile); if (dwret != SCARD_S_SUCCESS) goto ret_cleanup; dwret = md_set_cmapfile(pCardData, cmapfile); if (dwret != SCARD_S_SUCCESS) goto ret_cleanup; #ifdef OPENSSL_VERSION_NUMBER logprintf(pCardData, 3, "MD virtual file system initialized; OPENSSL_VERSION_NUMBER 0x%lX\n", OPENSSL_VERSION_NUMBER); #else logprintf(pCardData, 3, "MD virtual file system initialized; Without OPENSSL\n"); #endif return SCARD_S_SUCCESS; ret_cleanup: md_fs_finalize(pCardData); return dwret; } /* Create SC context */ static DWORD md_create_context(PCARD_DATA pCardData, VENDOR_SPECIFIC *vs) { sc_context_param_t ctx_param; int r; if (!pCardData) return SCARD_E_INVALID_PARAMETER; logprintf(pCardData, 3, "create sc ccontext\n"); vs->ctx = NULL; memset(&ctx_param, 0, sizeof(ctx_param)); ctx_param.ver = 1; ctx_param.app_name = "cardmod"; r = sc_context_create(&(vs->ctx), &ctx_param); if (r) { logprintf(pCardData, 0, "Failed to establish context: %s\n", sc_strerror(r)); return SCARD_F_UNKNOWN_ERROR; } logprintf(pCardData, 3, "sc context created\n"); return SCARD_S_SUCCESS; } static DWORD md_card_capabilities(PCARD_DATA pCardData, PCARD_CAPABILITIES pCardCapabilities) { if (!pCardCapabilities) return SCARD_E_INVALID_PARAMETER; if (pCardCapabilities->dwVersion != CARD_CAPABILITIES_CURRENT_VERSION && pCardCapabilities->dwVersion != 0) return ERROR_REVISION_MISMATCH; pCardCapabilities->dwVersion = CARD_CAPABILITIES_CURRENT_VERSION; pCardCapabilities->fCertificateCompression = TRUE; /* a read only card cannot generate new keys */ pCardCapabilities->fKeyGen = ! md_is_read_only(pCardData); return SCARD_S_SUCCESS; } static DWORD md_free_space(PCARD_DATA pCardData, PCARD_FREE_SPACE_INFO pCardFreeSpaceInfo) { VENDOR_SPECIFIC *vs; int count, idx; if (!pCardData || !pCardFreeSpaceInfo) return SCARD_E_INVALID_PARAMETER; if (pCardFreeSpaceInfo->dwVersion > CARD_FREE_SPACE_INFO_CURRENT_VERSION ) return ERROR_REVISION_MISMATCH; vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); if (!vs) return SCARD_E_INVALID_PARAMETER; /* Count free containers */ for (idx=0, count=0; idxp15_containers[idx].prkey_obj) count++; pCardFreeSpaceInfo->dwVersion = CARD_FREE_SPACE_INFO_CURRENT_VERSION; pCardFreeSpaceInfo->dwBytesAvailable = CARD_DATA_VALUE_UNKNOWN; pCardFreeSpaceInfo->dwKeyContainersAvailable = count; pCardFreeSpaceInfo->dwMaxKeyContainers = MD_MAX_KEY_CONTAINERS; return SCARD_S_SUCCESS; } /* Check the new key to be created for the compatibility with card: * - for the key to be generated the card needs to support the mechanism and size; * - for the key to be imported checked also the validity of supplied key blob. */ static DWORD md_check_key_compatibility(PCARD_DATA pCardData, DWORD flags, DWORD key_type, DWORD key_size, BYTE *pbKeyData) { VENDOR_SPECIFIC *vs; struct sc_algorithm_info *algo_info; unsigned int count, key_algo; if (!pCardData) return SCARD_E_INVALID_PARAMETER; switch(key_type) { case AT_SIGNATURE: case AT_KEYEXCHANGE: key_algo = SC_ALGORITHM_RSA; break; case AT_ECDHE_P256 : case AT_ECDHE_P384 : case AT_ECDHE_P521 : case AT_ECDSA_P256 : case AT_ECDSA_P384 : case AT_ECDSA_P521 : key_algo = SC_ALGORITHM_EC; break; default: logprintf(pCardData, 3, "Unsupported key type: 0x%lX\n", (unsigned long)key_type); return SCARD_E_UNSUPPORTED_FEATURE; } vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); if (!vs) return SCARD_E_INVALID_PARAMETER; if (flags & CARD_CREATE_CONTAINER_KEY_IMPORT) { if (key_algo == SC_ALGORITHM_RSA) { PUBLICKEYSTRUC *pub_struc = (PUBLICKEYSTRUC *)pbKeyData; RSAPUBKEY *pub_rsa = (RSAPUBKEY *)(pbKeyData + sizeof(PUBLICKEYSTRUC)); if (!pub_struc) { logprintf(pCardData, 3, "No data for the key import operation\n"); return SCARD_E_INVALID_PARAMETER; } else if (pub_struc->bType != PRIVATEKEYBLOB) { logprintf(pCardData, 3, "Invalid blob data for the key import operation\n"); return SCARD_E_INVALID_PARAMETER; } else if ((key_type == AT_KEYEXCHANGE) && (pub_struc->aiKeyAlg != CALG_RSA_KEYX)) { logprintf(pCardData, 3, "Expected KEYEXCHANGE type of blob\n"); return SCARD_E_INVALID_PARAMETER; } else if ((key_type == AT_SIGNATURE) && (pub_struc->aiKeyAlg != CALG_RSA_SIGN)) { logprintf(pCardData, 3, "Expected KEYSIGN type of blob\n"); return SCARD_E_INVALID_PARAMETER; } if (pub_rsa->magic == BCRYPT_RSAPUBLIC_MAGIC || pub_rsa->magic == BCRYPT_RSAPRIVATE_MAGIC) { key_size = pub_rsa->bitlen; } else { logprintf(pCardData, 3, "'Magic' control failed\n"); return SCARD_E_INVALID_PARAMETER; } logprintf(pCardData, 3, "Set key size to %lu\n", (unsigned long)key_size); } else if (key_algo == SC_ALGORITHM_EC) { BCRYPT_ECCKEY_BLOB *pub_ecc = (BCRYPT_ECCKEY_BLOB *)pbKeyData; switch(key_type) { case AT_ECDSA_P256: if (pub_ecc->dwMagic != BCRYPT_ECDSA_PRIVATE_P256_MAGIC) { logprintf(pCardData, 3, "Expected AT_ECDSA_P256 magic\n"); return SCARD_E_INVALID_PARAMETER; } key_size = 256; break; case AT_ECDSA_P384: if (pub_ecc->dwMagic != BCRYPT_ECDSA_PRIVATE_P384_MAGIC) { logprintf(pCardData, 3, "Expected AT_ECDSA_P384 magic\n"); return SCARD_E_INVALID_PARAMETER; } key_size = 384; break; case AT_ECDSA_P521: if (pub_ecc->dwMagic != BCRYPT_ECDSA_PRIVATE_P521_MAGIC) { logprintf(pCardData, 3, "Expected AT_ECDSA_P521 magic\n"); return SCARD_E_INVALID_PARAMETER; } key_size = 521; break; case AT_ECDHE_P256: if (pub_ecc->dwMagic != BCRYPT_ECDH_PRIVATE_P256_MAGIC) { logprintf(pCardData, 3, "Expected AT_ECDHE_P256 magic\n"); return SCARD_E_INVALID_PARAMETER; } key_size = 256; break; case AT_ECDHE_P384: if (pub_ecc->dwMagic != BCRYPT_ECDH_PRIVATE_P384_MAGIC) { logprintf(pCardData, 3, "Expected AT_ECDHE_P384 magic\n"); return SCARD_E_INVALID_PARAMETER; } key_size = 384; break; case AT_ECDHE_P521: if (pub_ecc->dwMagic != BCRYPT_ECDH_PRIVATE_P521_MAGIC) { logprintf(pCardData, 3, "Expected AT_ECDHE_P521 magic\n"); return SCARD_E_INVALID_PARAMETER; } key_size = 521; break; } } logprintf(pCardData, 3, "Set key size to %lu\n", (unsigned long)key_size); } count = vs->p15card->card->algorithm_count; for (algo_info = vs->p15card->card->algorithms; count--; algo_info++) { if (algo_info->algorithm != key_algo || algo_info->key_length != key_size) continue; logprintf(pCardData, 3, "Key compatible with the card capabilities\n"); return SCARD_S_SUCCESS; } logprintf(pCardData, 3, "No card support for key(type:0x%lX,size:0x%lX)\n", (unsigned long)key_type, (unsigned long)key_size); return SCARD_E_UNSUPPORTED_FEATURE; } static DWORD 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; struct sc_profile *profile = NULL; struct sc_pkcs15_object *pin_obj = NULL; struct sc_pkcs15_auth_info *auth_info = NULL; struct sc_app_info *app_info = NULL; struct sc_pkcs15init_keygen_args keygen_args; struct sc_pkcs15init_pubkeyargs pub_args; struct md_pkcs15_container *cont = NULL; int rv; 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)); memset(&keygen_args, 0, sizeof(keygen_args)); keygen_args.prkey_args.label = szGuid; keygen_args.pubkey_label = szGuid; if (key_type == AT_SIGNATURE) { keygen_args.prkey_args.key.algorithm = SC_ALGORITHM_RSA; pub_args.key.algorithm = SC_ALGORITHM_RSA; keygen_args.prkey_args.x509_usage = MD_KEY_USAGE_SIGNATURE; } else if (key_type == AT_KEYEXCHANGE) { keygen_args.prkey_args.key.algorithm = SC_ALGORITHM_RSA; pub_args.key.algorithm = SC_ALGORITHM_RSA; keygen_args.prkey_args.x509_usage = MD_KEY_USAGE_KEYEXCHANGE; } else if ((key_type == AT_ECDSA_P256) || (key_type == AT_ECDSA_P384) || (key_type == AT_ECDSA_P521)) { keygen_args.prkey_args.key.algorithm = SC_ALGORITHM_EC; pub_args.key.algorithm = SC_ALGORITHM_EC; keygen_args.prkey_args.x509_usage = MD_KEY_USAGE_SIGNATURE; } else if ((key_type == AT_ECDHE_P256) || (key_type == AT_ECDHE_P384) || (key_type == AT_ECDHE_P521)) { keygen_args.prkey_args.key.algorithm = SC_ALGORITHM_EC; pub_args.key.algorithm = SC_ALGORITHM_EC; keygen_args.prkey_args.x509_usage = MD_KEY_USAGE_KEYEXCHANGE_ECC; } else { logprintf(pCardData, 3, "MdGenerateKey(): unsupported key type: 0x%lX\n", (unsigned long)key_type); return SCARD_E_UNSUPPORTED_FEATURE; } if (pub_args.key.algorithm == SC_ALGORITHM_EC) { keygen_args.prkey_args.key.u.ec.params.field_length = key_size; if ((key_type == AT_ECDSA_P256)|| (key_type == AT_ECDHE_P256)) { keygen_args.prkey_args.key.u.ec.params.named_curve = "secp256r1"; keygen_args.prkey_args.key.u.ec.params.der.len = 10; keygen_args.prkey_args.key.u.ec.params.der.value = (unsigned char *)"\x06\x08\x2A\x86\x48\xCE\x3D\x03\x01\x07"; } else if ((key_type == AT_ECDSA_P384)|| (key_type == AT_ECDHE_P384)) { keygen_args.prkey_args.key.u.ec.params.named_curve = "secp384r1"; keygen_args.prkey_args.key.u.ec.params.der.len = 7; keygen_args.prkey_args.key.u.ec.params.der.value = (unsigned char *)"\x06\x05\x2B\x81\x04\x00\x22"; } else if ((key_type == AT_ECDSA_P521)|| (key_type == AT_ECDHE_P521)) { keygen_args.prkey_args.key.u.ec.params.named_curve = "secp521r1"; keygen_args.prkey_args.key.u.ec.params.der.len = 7; keygen_args.prkey_args.key.u.ec.params.der.value = (unsigned char *)"\x06\x05\x2B\x81\x04\x00\x23"; } } keygen_args.prkey_args.access_flags = MD_KEY_ACCESS; 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; rv = sc_lock(card); if (rv) { logprintf(pCardData, 3, "MdGenerateKey(): cannot lock card\n"); return SCARD_F_INTERNAL_ERROR; } app_info = vs->p15card->app; rv = sc_pkcs15init_bind(card, "pkcs15", NULL, NULL, &profile); if (rv) { logprintf(pCardData, 3, "MdGenerateKey(): PKCS#15 bind failed\n"); sc_unlock(card); return SCARD_F_INTERNAL_ERROR; } rv = sc_pkcs15init_finalize_profile(card, profile, app_info ? &app_info->aid : NULL); if (rv) { logprintf(pCardData, 3, "MdGenerateKey(): cannot finalize profile\n"); goto done; } sc_pkcs15init_set_p15card(profile, vs->p15card); cont = &(vs->p15_containers[idx]); /* use the Windows Guid as input to determine some characteristics of the key such as the label or the id */ md_contguid_build_key_args_from_cont_guid(pCardData, cont->guid, &(keygen_args.prkey_args)); if (keygen_args.prkey_args.label == NULL) { md_generate_guid(szGuid); keygen_args.prkey_args.label = szGuid; } keygen_args.pubkey_label = keygen_args.prkey_args.label; rv = sc_pkcs15init_generate_key(vs->p15card, profile, &keygen_args, key_size, &cont->prkey_obj); if (rv < 0) { logprintf(pCardData, 3, "MdGenerateKey(): key generation failed: sc-error %i\n", rv); goto done; } dwret = md_contguid_add_conversion(pCardData, cont->prkey_obj, cont->guid); if (dwret != SCARD_S_SUCCESS) return dwret; cont->id = ((struct sc_pkcs15_prkey_info *)cont->prkey_obj->data)->id; cont->index = idx; cont->flags = CONTAINER_MAP_VALID_CONTAINER; logprintf(pCardData, 3, "MdGenerateKey(): generated key(idx:%lu,id:%s,guid:%.*s)\n", (unsigned long)idx, sc_pkcs15_print_id(&cont->id), (int) sizeof cont->guid, cont->guid); done: sc_pkcs15init_unbind(profile); sc_unlock(card); return dwret; } static DWORD md_pkcs15_store_key(PCARD_DATA pCardData, DWORD idx, DWORD key_type, BYTE *blob, DWORD blob_size, PIN_ID PinId) { #ifdef ENABLE_OPENSSL VENDOR_SPECIFIC *vs; struct sc_card *card = NULL; struct sc_profile *profile = NULL; struct sc_pkcs15_object *pin_obj = NULL; struct sc_app_info *app_info = NULL; struct md_pkcs15_container *cont = NULL; struct sc_pkcs15init_prkeyargs prkey_args; struct sc_pkcs15init_pubkeyargs pubkey_args; BYTE *ptr = blob; EVP_PKEY *pkey=NULL; int rv; 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); if (!pkey) { logprintf(pCardData, 1, "MdStoreKey() MSBLOB key parse error"); return SCARD_E_INVALID_PARAMETER; } memset(&prkey_args, 0, sizeof(prkey_args)); rv = sc_pkcs15_convert_prkey(&prkey_args.key, pkey); if (rv) { logprintf(pCardData, 1, "MdStoreKey() cannot convert private key"); return SCARD_E_INVALID_PARAMETER; } memset(&pubkey_args, 0, sizeof(pubkey_args)); rv = sc_pkcs15_convert_pubkey(&pubkey_args.key, pkey); if (rv) { logprintf(pCardData, 1, "MdStoreKey() cannot convert public key"); return SCARD_E_INVALID_PARAMETER; } if (key_type == AT_SIGNATURE) { prkey_args.x509_usage = MD_KEY_USAGE_SIGNATURE; pubkey_args.x509_usage = MD_KEY_USAGE_SIGNATURE; } else if (key_type == AT_KEYEXCHANGE) { prkey_args.x509_usage = MD_KEY_USAGE_KEYEXCHANGE; pubkey_args.x509_usage = MD_KEY_USAGE_KEYEXCHANGE; } else { logprintf(pCardData, 3, "MdStoreKey(): unsupported key type: 0x%lX\n", (unsigned long)key_type); return SCARD_E_INVALID_PARAMETER; } prkey_args.access_flags = MD_KEY_ACCESS; pin_obj = vs->pin_objs[PinId]; prkey_args.auth_id = ((struct sc_pkcs15_auth_info *) pin_obj->data)->auth_id; rv = sc_lock(card); if (rv) { logprintf(pCardData, 3, "MdStoreKey(): cannot lock card\n"); return SCARD_F_INTERNAL_ERROR; } app_info = vs->p15card->app; rv = sc_pkcs15init_bind(card, "pkcs15", NULL, NULL, &profile); if (rv) { logprintf(pCardData, 3, "MdStoreKey(): PKCS#15 bind failed\n"); sc_unlock(card); return SCARD_F_INTERNAL_ERROR; } rv = sc_pkcs15init_finalize_profile(card, profile, app_info ? &app_info->aid : NULL); if (rv) { logprintf(pCardData, 3, "MdStoreKey(): cannot finalize profile\n"); goto done; } sc_pkcs15init_set_p15card(profile, vs->p15card); cont = &(vs->p15_containers[idx]); prkey_args.label = szGuid; /* use the Windows Guid as input to determine some characteristics of the key such as the label or the id */ md_contguid_build_key_args_from_cont_guid(pCardData, cont->guid, &prkey_args); memcpy(pubkey_args.id.value, prkey_args.id.value, prkey_args.id.len); pubkey_args.id.len = prkey_args.id.len; pubkey_args.label = prkey_args.label; if (prkey_args.label == szGuid) { md_generate_guid(szGuid); } pubkey_args.label = prkey_args.label; rv = sc_pkcs15init_store_private_key(vs->p15card, profile, &prkey_args, &cont->prkey_obj); if (rv < 0) { logprintf(pCardData, 3, "MdStoreKey(): private key store failed: sc-error %i\n", rv); goto done; } rv = sc_pkcs15init_store_public_key(vs->p15card, profile, &pubkey_args, &cont->pubkey_obj); if (rv < 0) { logprintf(pCardData, 3, "MdStoreKey(): public key store failed: sc-error %i\n", rv); goto done; } dwret = md_contguid_add_conversion(pCardData, cont->prkey_obj, cont->guid); if (dwret != SCARD_S_SUCCESS) return dwret; cont->id = ((struct sc_pkcs15_prkey_info *)cont->prkey_obj->data)->id; cont->index = idx; cont->flags |= CONTAINER_MAP_VALID_CONTAINER; logprintf(pCardData, 3, "MdStoreKey(): stored key(idx:%lu,id:%s,guid:%.*s)\n", (unsigned long)idx, sc_pkcs15_print_id(&cont->id), (int) sizeof cont->guid, cont->guid); done: sc_pkcs15init_unbind(profile); sc_unlock(card); return dwret; #else logprintf(pCardData, 1, "MD store key not supported\n"); return SCARD_E_UNSUPPORTED_FEATURE; #endif /* ENABLE_OPENSSL */ } static DWORD md_pkcs15_store_certificate(PCARD_DATA pCardData, char *file_name, unsigned char *blob, size_t len) { VENDOR_SPECIFIC *vs; struct md_pkcs15_container *cont = NULL; struct sc_card *card = NULL; struct sc_profile *profile = NULL; struct sc_app_info *app_info = NULL; struct sc_pkcs15_object *cert_obj; struct sc_pkcs15init_certargs args; int rv, idx; DWORD dwret = SCARD_F_INTERNAL_ERROR; if (!pCardData) return SCARD_E_INVALID_PARAMETER; 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)); args.der_encoded.value = blob; args.der_encoded.len = len; args.update = 1; /* use container's ID as ID of certificate to store */ idx = -1; if(sscanf(file_name, "ksc%d", &idx) > 0) { } else if(sscanf(file_name, "kxc%d", &idx) > 0) { } if (idx >= 0 && idx < MD_MAX_KEY_CONTAINERS) { cont = &(vs->p15_containers[idx]); args.id = cont->id; logprintf(pCardData, 3, "MdStoreCert(): store certificate(idx:%i,id:%s)\n", idx, sc_pkcs15_print_id(&cont->id)); } rv = sc_lock(card); if (rv) { logprintf(pCardData, 3, "MdStoreCert(): cannot lock card\n"); return SCARD_F_INTERNAL_ERROR; } app_info = vs->p15card->app; rv = sc_pkcs15init_bind(card, "pkcs15", NULL, NULL, &profile); if (rv) { logprintf(pCardData, 3, "MdStoreCert(): PKCS#15 bind failed\n"); sc_unlock(card); return SCARD_F_INTERNAL_ERROR; } rv = sc_pkcs15init_finalize_profile(card, profile, app_info ? &app_info->aid : NULL); if (rv) { logprintf(pCardData, 3, "MdStoreCert(): cannot finalize profile\n"); goto done; } sc_pkcs15init_set_p15card(profile, vs->p15card); rv = sc_pkcs15init_store_certificate(vs->p15card, profile, &args, &cert_obj); if (rv < 0) { logprintf(pCardData, 3, "MdStoreCert(): cannot store certificate: sc-error %i\n", rv); goto done; } dwret = SCARD_S_SUCCESS; done: sc_pkcs15init_unbind(profile); sc_unlock(card); return dwret; } static DWORD md_query_key_sizes(PCARD_DATA pCardData, DWORD dwKeySpec, CARD_KEY_SIZES *pKeySizes) { VENDOR_SPECIFIC *vs = NULL; struct sc_algorithm_info* algo_info; int count = 0, i, keysize = 0, flag; if (!pKeySizes) return SCARD_E_INVALID_PARAMETER; if (pKeySizes->dwVersion != CARD_KEY_SIZES_CURRENT_VERSION && pKeySizes->dwVersion != 0) return ERROR_REVISION_MISMATCH; 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; pKeySizes->dwMinimumBitlen = 0; pKeySizes->dwDefaultBitlen = 0; pKeySizes->dwMaximumBitlen = 0; pKeySizes->dwIncrementalBitlen = 0; /* 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++) { algo_info = vs->p15card->card->algorithms + i; if (algo_info->algorithm == SC_ALGORITHM_RSA) { if (pKeySizes->dwMinimumBitlen == 0 || pKeySizes->dwMinimumBitlen > algo_info->key_length) { pKeySizes->dwMinimumBitlen = algo_info->key_length; } if (pKeySizes->dwMaximumBitlen == 0 || pKeySizes->dwMaximumBitlen < algo_info->key_length) { pKeySizes->dwMaximumBitlen = algo_info->key_length; } if (algo_info->key_length == 2048) { pKeySizes->dwDefaultBitlen = algo_info->key_length; } if (algo_info->key_length == 1536) { pKeySizes->dwIncrementalBitlen = 512; } } } if (pKeySizes->dwMinimumBitlen == 0) { logprintf(pCardData, 0, "No RSA key found\n"); return SCARD_E_INVALID_PARAMETER; } if (pKeySizes->dwDefaultBitlen == 0) { logprintf(pCardData, 3, "No 2048 key found\n"); pKeySizes->dwDefaultBitlen = pKeySizes->dwMaximumBitlen; } if (pKeySizes->dwIncrementalBitlen == 0) { pKeySizes->dwIncrementalBitlen = 1024; } } else { keysize = 0; 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; /* 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; break; } if ((dwKeySpec == AT_ECDHE_P384) && (algo_info->key_length == 384) && (algo_info->flags & flag)) { keysize = 384; break; } if ((dwKeySpec == AT_ECDHE_P521) && (algo_info->key_length == 521) && (algo_info->flags & flag)) { keysize = 521; break; } /* ECDSA */ 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; /* 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; } if ((dwKeySpec == AT_ECDSA_P384) && (algo_info->key_length == 384) && (algo_info->flags & flag)) { keysize = 384; break; } if ((dwKeySpec == AT_ECDSA_P521) && (algo_info->key_length == 521) && (algo_info->flags & flag)) { keysize = 521; 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; } } } logprintf(pCardData, 3, "Key compatible with the card capabilities\n"); logprintf(pCardData, 3, " dwMinimumBitlen: %lu\n", (unsigned long)pKeySizes->dwMinimumBitlen); logprintf(pCardData, 3, " dwDefaultBitlen: %lu\n", (unsigned long)pKeySizes->dwDefaultBitlen); logprintf(pCardData, 3, " dwMaximumBitlen: %lu\n", (unsigned long)pKeySizes->dwMaximumBitlen); logprintf(pCardData, 3, " dwIncrementalBitlen: %lu\n", (unsigned long)pKeySizes->dwIncrementalBitlen); return SCARD_S_SUCCESS; } static DWORD WINAPI md_dialog_perform_pin_operation_thread(PVOID lpParameter) { /* unstack the parameters */ LONG_PTR* parameter = (LONG_PTR*) lpParameter; int operation = (int) parameter[0]; struct sc_pkcs15_card *p15card = (struct sc_pkcs15_card *) parameter[1]; struct sc_pkcs15_object *pin_obj = (struct sc_pkcs15_object *) parameter[2]; const u8 *pin1 = (const u8 *) parameter[3]; size_t pin1len = parameter[4]; const u8 *pin2 = (const u8 *) parameter[5]; size_t *pin2len = (size_t *) parameter[6]; int rv = 0; switch (operation) { case SC_PIN_CMD_VERIFY: rv = sc_pkcs15_verify_pin(p15card, pin_obj, pin1, pin1len); break; case SC_PIN_CMD_GET_SESSION_PIN: rv = sc_pkcs15_verify_pin_with_session_pin(p15card, pin_obj, pin1, pin1len, pin2, pin2len); break; case SC_PIN_CMD_CHANGE: rv = sc_pkcs15_change_pin(p15card, pin_obj, pin1, pin1len,pin2, pin2len ? *pin2len : 0); break; case SC_PIN_CMD_UNBLOCK: rv = sc_pkcs15_unblock_pin(p15card, pin_obj, pin1, pin1len,pin2, pin2len ? *pin2len : 0); break; default: rv = (DWORD) ERROR_INVALID_PARAMETER; break; } if (parameter[10] != 0) { EndDialog((HWND) parameter[10], rv); } return (DWORD) rv; } static const char *md_get_ui_str(PCARD_DATA pCardData, enum ui_str id) { 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: { 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); } } } 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 */ param = GetWindowLongPtr(hWnd, GWLP_USERDATA); if (param) { HANDLE hThread = (HANDLE)((LONG_PTR*)param)[9]; CloseHandle(hThread); } break; } /* don't close the Task Dialog */ return S_FALSE; } 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, DWORD role) { 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; parameter[2] = (LONG_PTR)pin_obj; parameter[3] = (LONG_PTR)pin1; parameter[4] = (LONG_PTR)pin1len; parameter[5] = (LONG_PTR)pin2; parameter[6] = (LONG_PTR)pin2len; parameter[7] = (LONG_PTR)pCardData; 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 this is the only way to display a modal dialog attached to a parent (hwndParent != 0) */ 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; } static DWORD md_translate_OpenSC_to_Windows_error(int OpenSCerror, DWORD dwDefaulCode) { switch(OpenSCerror) { /* Errors related to reader operation */ case SC_ERROR_READER: return SCARD_E_PROTO_MISMATCH; case SC_ERROR_NO_READERS_FOUND: return SCARD_E_NO_READERS_AVAILABLE; case SC_ERROR_CARD_NOT_PRESENT: return SCARD_E_NO_SMARTCARD; case SC_ERROR_TRANSMIT_FAILED: return SCARD_E_NOT_TRANSACTED; case SC_ERROR_CARD_REMOVED: return SCARD_W_REMOVED_CARD; case SC_ERROR_CARD_RESET: return SCARD_W_RESET_CARD; case SC_ERROR_KEYPAD_CANCELLED: return SCARD_W_CANCELLED_BY_USER; case SC_ERROR_KEYPAD_MSG_TOO_LONG: return SCARD_W_CARD_NOT_AUTHENTICATED; case SC_ERROR_KEYPAD_PIN_MISMATCH: return SCARD_E_INVALID_CHV; case SC_ERROR_KEYPAD_TIMEOUT: return ERROR_TIMEOUT; case SC_ERROR_EVENT_TIMEOUT: return SCARD_E_TIMEOUT; case SC_ERROR_CARD_UNRESPONSIVE: return SCARD_W_UNRESPONSIVE_CARD; case SC_ERROR_READER_LOCKED: return SCARD_E_SHARING_VIOLATION; /* Resulting from a card command or related to the card*/ case SC_ERROR_INCORRECT_PARAMETERS: return SCARD_E_INVALID_PARAMETER; case SC_ERROR_MEMORY_FAILURE: case SC_ERROR_NOT_ENOUGH_MEMORY: return SCARD_E_NO_MEMORY; case SC_ERROR_NOT_ALLOWED: return SCARD_W_SECURITY_VIOLATION; case SC_ERROR_AUTH_METHOD_BLOCKED: return SCARD_W_CHV_BLOCKED; case SC_ERROR_PIN_CODE_INCORRECT: return SCARD_W_WRONG_CHV; /* Returned by OpenSC library when called with invalid arguments */ case SC_ERROR_INVALID_ARGUMENTS: return ERROR_INVALID_PARAMETER; case SC_ERROR_BUFFER_TOO_SMALL: return NTE_BUFFER_TOO_SMALL; /* Resulting from OpenSC internal operation */ case SC_ERROR_INTERNAL: return ERROR_INTERNAL_ERROR; case SC_ERROR_NOT_SUPPORTED: return SCARD_E_UNSUPPORTED_FEATURE; case SC_ERROR_NOT_IMPLEMENTED: return ERROR_CALL_NOT_IMPLEMENTED; default: return dwDefaulCode; } } DWORD WINAPI CardDeleteContext(__inout PCARD_DATA pCardData) { VENDOR_SPECIFIC *vs = NULL; CRITICAL_SECTION hScard_lock; MD_FUNC_CALLED(pCardData, 1); if(!pCardData) MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); logprintf(pCardData, 1, "\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, (size_t)pCardData->hScard, (size_t)pCardData->hSCardCtx); vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); if(!vs) 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); vs->ctx = NULL; } logprintf(pCardData, 1, "**********************************************************************\n"); pCardData->pfnCspFree(pCardData->pvVendorSpecific); pCardData->pvVendorSpecific = NULL; LeaveCriticalSection(&hScard_lock); DeleteCriticalSection(&hScard_lock); MD_FUNC_RETURN(pCardData, 1, SCARD_S_SUCCESS); } DWORD WINAPI CardQueryCapabilities(__in PCARD_DATA pCardData, __inout PCARD_CAPABILITIES pCardCapabilities) { 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 || !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) { goto err; } err: unlock(pCardData); MD_FUNC_RETURN(pCardData, 1, dwret); } DWORD WINAPI CardDeleteContainer(__in PCARD_DATA pCardData, __in BYTE bContainerIndex, __in DWORD dwReserved) { 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 || !lock(pCardData)) MD_FUNC_RETURN(pCardData, 1, 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"); dwret = SCARD_E_UNSUPPORTED_FEATURE; goto err; } vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); 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) { logprintf(pCardData, 1, "private key deletion failed\n"); goto err; } dwret = md_pkcs15_delete_object(pCardData, cont->pubkey_obj); if (dwret != SCARD_S_SUCCESS) { logprintf(pCardData, 1, "public key deletion failed\n"); goto err; } ZeroMemory(cont, sizeof(struct md_pkcs15_container)); logprintf(pCardData, 1, "key deleted\n"); err: unlock(pCardData); 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; 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,pin:%u)\n", (unsigned int)bContainerIndex, (unsigned long)dwFlags, (unsigned long)dwKeySpec, (unsigned long)dwKeySize, pbKeyData, (unsigned int)PinId); if (pbKeyData) { logprintf(pCardData, 7, "Key data\n"); loghex(pCardData, 7, pbKeyData, dwKeySize); } dwret = md_check_key_compatibility(pCardData, dwFlags, dwKeySpec, dwKeySize, pbKeyData); if (dwret != SCARD_S_SUCCESS) { logprintf(pCardData, 1, "check key compatibility failed\n"); goto err; } if (!md_is_supports_container_key_gen(pCardData)) { logprintf(pCardData, 1, "Denied 'generate key' mechanism to create container.\n"); dwFlags &= ~CARD_CREATE_CONTAINER_KEY_GEN; } if (!md_is_supports_container_key_import(pCardData)) { logprintf(pCardData, 1, "Denied 'import key' mechanism to create container.\n"); dwFlags &= ~CARD_CREATE_CONTAINER_KEY_IMPORT; } if (!dwFlags) { logprintf(pCardData, 1, "Unsupported create container mechanism.\n"); dwret = SCARD_E_UNSUPPORTED_FEATURE; goto err; } if (dwFlags & CARD_CREATE_CONTAINER_KEY_GEN) { dwret = md_pkcs15_generate_key(pCardData, bContainerIndex, dwKeySpec, dwKeySize, PinId); if (dwret != SCARD_S_SUCCESS) { logprintf(pCardData, 1, "key generation failed\n"); 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, PinId); if (dwret != SCARD_S_SUCCESS) { logprintf(pCardData, 1, "key store failed\n"); goto err; } logprintf(pCardData, 1, "key imported\n"); } else { logprintf(pCardData, 1, "Invalid dwFlags value: 0x%lX\n", (unsigned long)dwFlags); dwret = SCARD_E_INVALID_PARAMETER; goto err; } 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; RSAPUBKEY rsapubkey; } PUBRSAKEYSTRUCT_BASE; DWORD WINAPI CardGetContainerInfo(__in PCARD_DATA pCardData, __in BYTE bContainerIndex, __in DWORD dwFlags, __inout PCONTAINER_INFO pContainerInfo) { VENDOR_SPECIFIC *vs = NULL; DWORD sz = 0; 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) 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(), (unsigned long)GetCurrentThreadId(), pCardData); 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); 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); ret = SCARD_E_NO_KEY_CONTAINER; goto err; } ret = SCARD_F_UNKNOWN_ERROR; prkey_info = (struct sc_pkcs15_prkey_info *)cont->prkey_obj->data; 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; } if (!pubkey_der.value && cont->pubkey_obj) { struct sc_pkcs15_pubkey *pubkey = NULL; logprintf(pCardData, 1, "now read public key '%.*s'\n", (int) sizeof cont->pubkey_obj->label, cont->pubkey_obj->label); rv = sc_pkcs15_read_pubkey(vs->p15card, cont->pubkey_obj, &pubkey); if (!rv) { rv = sc_pkcs15_encode_pubkey(vs->ctx, pubkey, &pubkey_der.value, &pubkey_der.len); if (rv) { logprintf(pCardData, 1, "encode public key error %d\n", rv); ret = SCARD_F_INTERNAL_ERROR; } else { logprintf(pCardData, 1, "public key encoded\n"); ret = SCARD_S_SUCCESS; } sc_pkcs15_free_pubkey(pubkey); } else { logprintf(pCardData, 1, "public key read error %d\n", rv); ret = SCARD_E_FILE_NOT_FOUND; } } 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), private_obj, &cert); if(!rv) { rv = sc_pkcs15_encode_pubkey(vs->ctx, cert->key, &pubkey_der.value, &pubkey_der.len); if (rv) { logprintf(pCardData, 1, "encode certificate public key error %d\n", rv); ret = SCARD_F_INTERNAL_ERROR; } else { logprintf(pCardData, 1, "certificate public key encoded\n"); ret = SCARD_S_SUCCESS; } sc_pkcs15_free_certificate(cert); } else { logprintf(pCardData, 1, "certificate '%u' read error %d\n", (unsigned int)bContainerIndex, rv); ret = SCARD_E_FILE_NOT_FOUND; } } if (!pubkey_der.value && (cont->size_sign || cont->size_key_exchange)) { logprintf(pCardData, 2, "cannot find public key\n"); 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); goto err; } logprintf(pCardData, 7, "SubjectPublicKeyInfo:\n"); loghex(pCardData, 7, pubkey_der.value, pubkey_der.len); if (prkey_info->modulus_length > 0) { logprintf(pCardData, 7, "Encoding RSA public key"); if (pubkey_der.len && pubkey_der.value) { sz = 0; /* get size */ CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, RSA_CSP_PUBLICKEYBLOB, pubkey_der.value, (DWORD) pubkey_der.len, 0, NULL, &sz); if (cont->size_sign) { PUBRSAKEYSTRUCT_BASE *publicKey = (PUBRSAKEYSTRUCT_BASE *)pCardData->pfnCspAlloc(sz); 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); publicKey->publickeystruc.aiKeyAlg = CALG_RSA_SIGN; pContainerInfo->cbSigPublicKey = sz; pContainerInfo->pbSigPublicKey = (PBYTE)publicKey; logprintf(pCardData, 3, "return info on SIGN_CONTAINER_INDEX %u\n", (unsigned int)bContainerIndex); } if (cont->size_key_exchange) { PUBRSAKEYSTRUCT_BASE *publicKey = (PUBRSAKEYSTRUCT_BASE*)pCardData->pfnCspAlloc(sz); 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); publicKey->publickeystruc.aiKeyAlg = CALG_RSA_KEYX; pContainerInfo->cbKeyExPublicKey = sz; pContainerInfo->pbKeyExPublicKey = (PBYTE)publicKey; logprintf(pCardData, 3, "return info on KEYX_CONTAINER_INDEX %u\n", (unsigned int)bContainerIndex); } } } else if (prkey_info->field_length > 0) { logprintf(pCardData, 7, "Encoding ECC public key"); if (pubkey_der.len > 2 && pubkey_der.value && pubkey_der.value[0] == 4 && pubkey_der.value[1] == pubkey_der.len -2) { BCRYPT_ECCKEY_BLOB *publicKey = NULL; DWORD dwMagic = 0; if (cont->size_sign) { sz = (DWORD) (sizeof(BCRYPT_ECCKEY_BLOB) + pubkey_der.len -3); switch(cont->size_sign) { case 256: dwMagic = BCRYPT_ECDSA_PUBLIC_P256_MAGIC; break; case 384: dwMagic = BCRYPT_ECDSA_PUBLIC_P384_MAGIC; break; case 521: dwMagic = BCRYPT_ECDSA_PUBLIC_P521_MAGIC; break; default: 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); ret = SCARD_F_INTERNAL_ERROR; goto err; } publicKey = (BCRYPT_ECCKEY_BLOB *)pCardData->pfnCspAlloc(sz); if (!publicKey) { ret = SCARD_E_NO_MEMORY; goto err; } publicKey->cbKey = (DWORD)(pubkey_der.len -3) /2; publicKey->dwMagic = dwMagic; pContainerInfo->cbSigPublicKey = sz; pContainerInfo->pbSigPublicKey = (PBYTE)publicKey; 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 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); switch(cont->size_key_exchange) { case 256: dwMagic = BCRYPT_ECDH_PUBLIC_P256_MAGIC; break; case 384: dwMagic = BCRYPT_ECDH_PUBLIC_P384_MAGIC; break; case 521: dwMagic = BCRYPT_ECDH_PUBLIC_P521_MAGIC; break; default: 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); ret = SCARD_F_INTERNAL_ERROR; goto err; } publicKey = (BCRYPT_ECCKEY_BLOB *)pCardData->pfnCspAlloc(sz); if (!publicKey) { ret = SCARD_E_NO_MEMORY; goto err; } publicKey->cbKey = (DWORD)(pubkey_der.len -3) /2; publicKey->dwMagic = dwMagic; pContainerInfo->cbKeyExPublicKey = sz; pContainerInfo->pbKeyExPublicKey = (PBYTE)publicKey; 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 cbKey:%u dwMagic:%u\n", (unsigned int)bContainerIndex, (unsigned int)publicKey->cbKey, (unsigned int)publicKey->dwMagic); } } } logprintf(pCardData, 7, "returns container(idx:%u) info\n", (unsigned int)bContainerIndex); 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, __in LPWSTR pwszUserId, __in_bcount(cbPin) PBYTE pbPin, __in DWORD cbPin, __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); logprintf(pCardData, 1, "CardAuthenticatePin '%S':%lu\n", NULLWSTR(pwszUserId), (unsigned long)cbPin); if (wcscmp(pwszUserId, wszCARD_USER_USER) == 0) { PinId = ROLE_USER; } else if (wcscmp(pwszUserId, wszCARD_USER_ADMIN) == 0) { PinId = ROLE_ADMIN; } else { MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); } if (pbPin == NULL) MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); MD_FUNC_RETURN(pCardData, 1, CardAuthenticateEx(pCardData, PinId, CARD_PIN_SILENT_CONTEXT, pbPin, cbPin, NULL, NULL, pcAttemptsRemaining)); } DWORD WINAPI CardGetChallenge(__in PCARD_DATA pCardData, __deref_out_bcount(*pcbChallengeData) PBYTE *ppbChallengeData, __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) MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); if (!ppbChallengeData || !pcbChallengeData || !lock(pCardData)) MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); dwret = check_card_reader_status(pCardData, "CardGetChallenge"); if (dwret != SCARD_S_SUCCESS) { goto err; } vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); if (!vs) { dwret = SCARD_E_INVALID_PARAMETER; goto err; } *pcbChallengeData = 8; *ppbChallengeData = (PBYTE) pCardData->pfnCspAlloc(8); if (!*ppbChallengeData) { dwret = SCARD_E_NO_MEMORY; goto err; } rv = sc_get_challenge(vs->p15card->card, *ppbChallengeData, 8); if (rv < 0) { logprintf(pCardData, 1, "Get challenge failed: %s\n", sc_strerror(rv)); pCardData->pfnCspFree(*ppbChallengeData); *ppbChallengeData = NULL; 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); err: unlock(pCardData); MD_FUNC_RETURN(pCardData, 1, dwret); } DWORD WINAPI CardAuthenticateChallenge(__in PCARD_DATA pCardData, __in_bcount(cbResponseData) PBYTE pbResponseData, __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"); MD_FUNC_RETURN(pCardData, 1, SCARD_E_UNSUPPORTED_FEATURE); } DWORD WINAPI CardUnblockPin(__in PCARD_DATA pCardData, __in LPWSTR pwszUserId, __in_bcount(cbAuthenticationData) PBYTE pbAuthenticationData, __in DWORD cbAuthenticationData, __in_bcount(cbNewPinData) PBYTE pbNewPinData, __in DWORD cbNewPinData, __in DWORD cRetryCount, __in DWORD dwFlags) { 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(), (unsigned long)GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardUnblockPin\n"); if (pwszUserId == NULL) { logprintf(pCardData, 1, "no user ID\n"); 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); r = SCARD_E_INVALID_PARAMETER; goto err; } if (wcscmp(wszCARD_USER_ADMIN, pwszUserId) == 0) { logprintf(pCardData, 1, "unlocking admin not supported\n"); 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"); 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"); dwFlags &= ~CARD_AUTHENTICATE_PIN_CHALLENGE_RESPONSE; } if (dwFlags) { logprintf(pCardData, 1, "flags of %x not supported\n", (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); 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); } DWORD WINAPI CardChangeAuthenticator(__in PCARD_DATA pCardData, __in LPWSTR pwszUserId, __in_bcount(cbCurrentAuthenticator) PBYTE pbCurrentAuthenticator, __in DWORD cbCurrentAuthenticator, __in_bcount(cbNewAuthenticator) PBYTE pbNewAuthenticator, __in DWORD cbNewAuthenticator, __in DWORD cRetryCount, __in DWORD dwFlags, __out_opt PDWORD pcAttemptsRemaining) { DWORD r = SCARD_S_SUCCESS; PIN_ID pinid; 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, "CardChangeAuthenticator\n"); 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"); r = SCARD_E_UNSUPPORTED_FEATURE; goto err; } else if (dwFlags != CARD_AUTHENTICATE_PIN_PIN){ r = SCARD_E_INVALID_PARAMETER; goto err; } 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); if (wcscmp(wszCARD_USER_USER, pwszUserId) == 0) pinid = ROLE_USER; else pinid = ROLE_ADMIN; 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); } /* 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 || !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) { /* 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"); 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)); MD_FUNC_RETURN(pCardData, 1, SCARD_E_UNSUPPORTED_FEATURE); } DWORD WINAPI CardCreateFile(__in PCARD_DATA pCardData, __in_opt LPSTR pszDirectoryName, __in LPSTR pszFileName, __in DWORD cbInitialCreationSize, __in CARD_FILE_ACCESS_CONDITION AccessCondition) { 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); logprintf(pCardData, 1, "CardCreateFile(%s::%s, size %lu, acl:0x%X) called\n", 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)); goto err; } dwret = md_fs_add_file(pCardData, &dir->files, pszFileName, AccessCondition, NULL, cbInitialCreationSize, NULL); if (dwret != SCARD_S_SUCCESS) goto err; err: unlock(pCardData); MD_FUNC_RETURN(pCardData, 1, dwret); } DWORD WINAPI CardReadFile(__in PCARD_DATA pCardData, __in_opt LPSTR pszDirectoryName, __in LPSTR pszFileName, __in DWORD dwFlags, __deref_out_bcount_opt(*pcbData) PBYTE *ppbData, __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 || !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) || dwFlags) { dwret = SCARD_E_INVALID_PARAMETER; goto err; } 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)); dwret = SCARD_E_FILE_NOT_FOUND; goto err; } if (!file->blob) { dwret = md_fs_read_content(pCardData, pszDirectoryName, file); if (dwret != SCARD_S_SUCCESS) goto err; } if (ppbData) { *ppbData = pCardData->pfnCspAlloc(file->size); if(!*ppbData) { dwret = SCARD_E_NO_MEMORY; goto err; } memcpy(*ppbData, file->blob, file->size); } if (pcbData) *pcbData = (DWORD)file->size; logprintf(pCardData, 7, "returns '%s' content:\n", NULLSTR(pszFileName)); loghex(pCardData, 7, file->blob, file->size); err: unlock(pCardData); MD_FUNC_RETURN(pCardData, 1, dwret); } DWORD WINAPI CardWriteFile(__in PCARD_DATA pCardData, __in_opt LPSTR pszDirectoryName, __in LPSTR pszFileName, __in DWORD dwFlags, __in_bcount(cbData) PBYTE pbData, __in DWORD cbData) { struct md_file *file = NULL; DWORD dwret; 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)); 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", (unsigned long)cbData); loghex(pCardData, 2, pbData, cbData); } 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)); dwret = SCARD_E_FILE_NOT_FOUND; goto err; } logprintf(pCardData, 7, "set content of '%s' to:\n", NULLSTR(pszFileName)); loghex(pCardData, 7, pbData, cbData); dwret = md_fs_set_content(pCardData, file, pbData, cbData); if (dwret != SCARD_S_SUCCESS) { logprintf(pCardData, 1, "cannot set file content: %lu\n", (unsigned long)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) goto err; logprintf(pCardData, 2, "md_pkcs15_store_certificate() OK\n"); } } logprintf(pCardData, 2, "write '%s' ok.\n", NULLSTR(pszFileName)); err: unlock(pCardData); MD_FUNC_RETURN(pCardData, 1, dwret); } DWORD WINAPI CardDeleteFile(__in PCARD_DATA pCardData, __in_opt LPSTR pszDirectoryName, __in LPSTR pszFileName, __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); logprintf(pCardData, 1, "CardDeleteFile(%s, %s) called\n", NULLSTR(pszDirectoryName), NULLSTR(pszFileName)); if(!pCardData || !lock(pCardData)) MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); 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); goto err; } err: unlock(pCardData); MD_FUNC_RETURN(pCardData, 1, dwret); } DWORD WINAPI CardEnumFiles(__in PCARD_DATA pCardData, __in_opt LPSTR pszDirectoryName, __deref_out_ecount(*pdwcbFileName) LPSTR *pmszFileNames, __out LPDWORD pdwcbFileName, __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 || !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); 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)); if (!pszDirectoryName || !strlen(pszDirectoryName)) dir = &vs->root; else md_fs_find_directory(pCardData, NULL, pszDirectoryName, &dir); if (!dir) { logprintf(pCardData, 2, "enum files() failed: directory '%s' not found\n", NULLSTR(pszDirectoryName)); dwret = SCARD_E_FILE_NOT_FOUND; goto err; } file = dir->files; for (offs = 0; file != NULL && offs < sizeof(mstr) - 10;) { logprintf(pCardData, 2, "enum files(): file name '%s'\n", file->name); strlcpy(mstr + offs, (char *)file->name, sizeof(mstr) - offs); offs += strlen((char *)file->name) + 1; file = file->next; } mstr[offs] = 0; offs += 1; *pmszFileNames = (LPSTR)(*pCardData->pfnCspAlloc)(offs); if (*pmszFileNames == NULL) { dwret = SCARD_E_NO_MEMORY; goto err; } CopyMemory(*pmszFileNames, mstr, offs); *pdwcbFileName = (DWORD) offs; err: unlock(pCardData); MD_FUNC_RETURN(pCardData, 1, dwret); } DWORD WINAPI CardGetFileInfo(__in PCARD_DATA pCardData, __in_opt LPSTR pszDirectoryName, __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)); dwret = SCARD_E_FILE_NOT_FOUND; goto err; } pCardFileInfo->dwVersion = CARD_FILE_INFO_CURRENT_VERSION; pCardFileInfo->cbFileSize = (DWORD) file->size; pCardFileInfo->AccessCondition = file->acl; err: unlock(pCardData); MD_FUNC_RETURN(pCardData, 1, dwret); } DWORD WINAPI CardQueryFreeSpace(__in PCARD_DATA pCardData, __in DWORD dwFlags, __inout PCARD_FREE_SPACE_INFO pCardFreeSpaceInfo) { 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, "CardQueryFreeSpace %p, dwFlags=%lX, version=%lX\n", pCardFreeSpaceInfo, (unsigned long)dwFlags, (unsigned long)pCardFreeSpaceInfo->dwVersion); if (!pCardData || !lock(pCardData)) MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); 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"); goto err; } logprintf(pCardData, 7, "FreeSpace:\n"); loghex(pCardData, 7, (BYTE *)pCardFreeSpaceInfo, sizeof(*pCardFreeSpaceInfo)); err: unlock(pCardData); MD_FUNC_RETURN(pCardData, 1, dwret); } DWORD WINAPI CardQueryKeySizes(__in PCARD_DATA pCardData, __in DWORD dwKeySpec, __in DWORD dwFlags, __inout PCARD_KEY_SIZES pKeySizes) { 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, "CardQueryKeySizes dwKeySpec=%lX, dwFlags=%lX, version=%lX\n", (unsigned long)dwKeySpec, (unsigned long)dwFlags, pKeySizes ? (unsigned long)pKeySizes->dwVersion : 0); 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) goto err; logprintf(pCardData, 7, "pKeySizes:\n"); loghex(pCardData, 7, (BYTE *)pKeySizes, sizeof(*pKeySizes)); err: unlock(pCardData); MD_FUNC_RETURN(pCardData, 1, dwret); } DWORD WINAPI CardRSADecrypt(__in PCARD_DATA pCardData, __inout PCARD_RSA_DECRYPT_INFO pInfo) { 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 || !pInfo || pInfo->pbData == NULL) MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); if (pInfo->dwVersion > CARD_RSA_KEY_DECRYPT_INFO_CURRENT_VERSION) MD_FUNC_RETURN(pCardData, 1, ERROR_REVISION_MISMATCH); if ( pInfo->dwVersion < CARD_RSA_KEY_DECRYPT_INFO_CURRENT_VERSION && pCardData->dwVersion == CARD_DATA_CURRENT_VERSION) MD_FUNC_RETURN(pCardData, 1, ERROR_REVISION_MISMATCH); if (pInfo->dwKeySpec != AT_KEYEXCHANGE) 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) { dwret = SCARD_E_NO_KEY_CONTAINER; goto err; } logprintf(pCardData, 2, "CardRSADecrypt dwVersion=%lu, bContainerIndex=%u, dwKeySpec=%lu pbData=%p, cbData=%lu\n", (unsigned long)pInfo->dwVersion, (unsigned int)pInfo->bContainerIndex, (unsigned long)pInfo->dwKeySpec, pInfo->pbData, (unsigned long)pInfo->cbData); if (pInfo->dwVersion >= CARD_RSA_KEY_DECRYPT_INFO_VERSION_TWO) logprintf(pCardData, 2, " pPaddingInfo=%p dwPaddingType=0x%08X\n", pInfo->pPaddingInfo, (unsigned int)pInfo->dwPaddingType); pkey = vs->p15_containers[pInfo->bContainerIndex].prkey_obj; if (!pkey) { logprintf(pCardData, 2, "CardRSADecrypt prkey not found\n"); dwret = SCARD_E_NO_KEY_CONTAINER; goto err; } /* input and output buffers are always the same size */ pbuf = pCardData->pfnCspAlloc(pInfo->cbData); if (!pbuf) { dwret = SCARD_E_NO_MEMORY; goto err; } pbuf2 = pCardData->pfnCspAlloc(pInfo->cbData); if (!pbuf2) { pCardData->pfnCspFree(pbuf); dwret = SCARD_E_NO_MEMORY; goto err; } /*inversion donnees*/ for(ui = 0; ui < pInfo->cbData; ui++) pbuf[ui] = pInfo->pbData[pInfo->cbData-ui-1]; logprintf(pCardData, 2, "Data to be decrypted (inverted):\n"); loghex(pCardData, 7, pbuf, pInfo->cbData); prkey_info = (struct sc_pkcs15_prkey_info *)(pkey->data); alg_info = sc_card_find_rsa_alg(vs->p15card->card, (unsigned int) prkey_info->modulus_length); if (!alg_info) { logprintf(pCardData, 2, "Cannot get appropriate RSA card algorithm for key size %"SC_FORMAT_LEN_SIZE_T"u\n", prkey_info->modulus_length); pCardData->pfnCspFree(pbuf); pCardData->pfnCspFree(pbuf2); dwret = SCARD_F_INTERNAL_ERROR; goto err; } /* 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); 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 | 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) { logprintf(pCardData, 2, "sc_pkcs15_decipher: DECRYPT-INFO dwVersion=%lu\n", (unsigned long)pInfo->dwVersion); if (pInfo->dwPaddingType == CARD_PADDING_PKCS1) { unsigned int temp = pInfo->cbData; logprintf(pCardData, 2, "sc_pkcs15_decipher: stripping PKCS1 padding\n"); r = sc_pkcs1_strip_02_padding_constant_time(vs->ctx, prkey_info->modulus_length / 8, pbuf2, pInfo->cbData, pbuf2, &temp); pInfo->cbData = (DWORD) temp; 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); dwret = SCARD_F_INTERNAL_ERROR; goto err; } } } } 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_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 */ if ((pInfo->dwVersion < CARD_RSA_KEY_DECRYPT_INFO_VERSION_TWO) || ((pInfo->dwVersion >= CARD_RSA_KEY_DECRYPT_INFO_VERSION_TWO) && (pInfo->dwPaddingType == CARD_PADDING_NONE))) { if ((unsigned)r <= pInfo->cbData - 9) { /* add pkcs1 02 padding */ logprintf(pCardData, 2, "Add '%s' to the output data", "PKCS#1 BT02 padding"); memset(pbuf, 0x30, pInfo->cbData); *(pbuf + 0) = 0; *(pbuf + 1) = 2; memcpy(pbuf + pInfo->cbData - r, pbuf2, r); *(pbuf + pInfo->cbData - r - 1) = 0; memcpy(pbuf2, pbuf, pInfo->cbData); } } else if (pInfo->dwPaddingType == CARD_PADDING_PKCS1) { /* PKCS1 padding is already handled by the card... */ pInfo->cbData = r; } /* TODO: Handle OAEP padding if present - can call PFN_CSP_UNPAD_DATA */ } } else { logprintf(pCardData, 2, "CardRSADecrypt: no usable RSA algorithm\n"); pCardData->pfnCspFree(pbuf); pCardData->pfnCspFree(pbuf2); dwret = SCARD_E_INVALID_PARAMETER; goto err; } 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); dwret = md_translate_OpenSC_to_Windows_error(r, SCARD_E_INVALID_VALUE); goto err; } dwret = constant_time_select_s(good, SCARD_S_SUCCESS, SCARD_F_INTERNAL_ERROR); /*inversion donnees */ /* 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); 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 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) MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); if ( ( pInfo->dwVersion != CARD_SIGNING_INFO_BASIC_VERSION ) && ( pInfo->dwVersion != CARD_SIGNING_INFO_CURRENT_VERSION ) ) MD_FUNC_RETURN(pCardData, 1, ERROR_REVISION_MISMATCH); if ( pInfo->pbData == NULL ) MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); switch(pInfo->dwKeySpec) { case AT_SIGNATURE: case AT_KEYEXCHANGE: case AT_ECDSA_P256: case AT_ECDSA_P384: case AT_ECDSA_P521: case AT_ECDHE_P256: case AT_ECDHE_P384: case AT_ECDHE_P521: break; default: 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, (unsigned int)pInfo->bContainerIndex, (unsigned long)pInfo->dwKeySpec, (unsigned int)pInfo->dwSigningFlags, (unsigned int)pInfo->aiHashAlg); logprintf(pCardData, 7, "pInfo->pbData(%lu) ", (unsigned long)pInfo->cbData); loghex(pCardData, 7, pInfo->pbData, pInfo->cbData); hashAlg = pInfo->aiHashAlg; vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); 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) { dwret = SCARD_E_NO_KEY_CONTAINER; goto err; } prkey_info = (struct sc_pkcs15_prkey_info *)(pkey->data); logprintf(pCardData, 2, "pInfo->dwVersion = %lu\n", (unsigned long)pInfo->dwVersion); if (dataToSignLen < pInfo->cbData) { dwret = SCARD_E_INSUFFICIENT_BUFFER; goto err; } memcpy(dataToSign, pInfo->pbData, pInfo->cbData); dataToSignLen = pInfo->cbData; 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_crypt_flags |= SC_ALGORITHM_RSA_HASH_MD5; else if (hashAlg == CALG_SHA1) opt_crypt_flags |= SC_ALGORITHM_RSA_HASH_SHA1; else if (hashAlg == CALG_SSL3_SHAMD5) opt_crypt_flags |= SC_ALGORITHM_RSA_HASH_MD5_SHA1; else if (hashAlg == CALG_SHA_256) opt_crypt_flags |= SC_ALGORITHM_RSA_HASH_SHA256; else if (hashAlg == CALG_SHA_384) opt_crypt_flags |= SC_ALGORITHM_RSA_HASH_SHA384; else if (hashAlg == CALG_SHA_512) opt_crypt_flags |= SC_ALGORITHM_RSA_HASH_SHA512; else if (hashAlg == (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_RIPEMD160)) opt_crypt_flags |= SC_ALGORITHM_RSA_HASH_RIPEMD160; else if (hashAlg !=0) { logprintf(pCardData, 0, "bogus aiHashAlg %i\n", hashAlg); dwret = SCARD_E_UNSUPPORTED_FEATURE; goto err; } } 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; } } /* Compute output size */ if ( prkey_info->modulus_length > 0) { /* RSA */ pInfo->cbSignedData = (DWORD) prkey_info->modulus_length / 8; } else if ( prkey_info->field_length > 0) { switch(prkey_info->field_length) { case 256: /* ECDSA_P256 */ pInfo->cbSignedData = 256 / 8 * 2; break; case 384: /* ECDSA_P384 */ pInfo->cbSignedData = 384 / 8 * 2; break; case 512: /* ECDSA_P512 : special case !!!*/ pInfo->cbSignedData = 132; break; default: logprintf(pCardData, 0, "unknown ECC key size %"SC_FORMAT_LEN_SIZE_T"u\n", prkey_info->field_length); 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"); dwret = SCARD_E_INVALID_VALUE; goto err; } logprintf(pCardData, 3, "pInfo->cbSignedData = %lu\n", (unsigned long)pInfo->cbSignedData); if(!(pInfo->dwSigningFlags&CARD_BUFFER_SIZE_ONLY)) { int r,i; BYTE *pbuf = NULL; DWORD lg; lg = pInfo->cbSignedData; logprintf(pCardData, 3, "lg = %lu\n", (unsigned long)lg); pbuf = pCardData->pfnCspAlloc(lg); if (!pbuf) { dwret = SCARD_E_NO_MEMORY; goto err; } logprintf(pCardData, 7, "Data to sign: "); loghex(pCardData, 7, dataToSign, dataToSignLen); pInfo->pbSignedData = (PBYTE) pCardData->pfnCspAlloc(pInfo->cbSignedData); if (!pInfo->pbSignedData) { pCardData->pfnCspFree(pbuf); dwret = SCARD_E_NO_MEMORY; goto err; } 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); 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++) pInfo->pbSignedData[i] = pbuf[r-i-1]; } else { for(i = 0; i < r; i++) pInfo->pbSignedData[i] = pbuf[i]; } pCardData->pfnCspFree(pbuf); logprintf(pCardData, 7, "Signature (inverted): "); loghex(pCardData, 7, pInfo->pbSignedData, pInfo->cbSignedData); } logprintf(pCardData, 3, "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), (size_t)pCardData->hScard, (size_t)pCardData->hSCardCtx); 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 = SC_ALGORITHM_ECDH_CDH_RAW; u8* out = 0; size_t outlen = 0; PBYTE pbPublicKey = NULL; 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) MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); if (!pAgreementInfo) MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); if ( pAgreementInfo->pbPublicKey == NULL ) MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); if (pAgreementInfo->dwVersion > CARD_DH_AGREEMENT_INFO_VERSION) MD_FUNC_RETURN(pCardData, 1, ERROR_REVISION_MISMATCH); if ( pAgreementInfo->dwVersion < CARD_DH_AGREEMENT_INFO_VERSION && pCardData->dwVersion == CARD_DATA_CURRENT_VERSION) 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) { 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, (unsigned int)pAgreementInfo->bContainerIndex, pAgreementInfo->pbPublicKey, (unsigned long)pAgreementInfo->dwPublicKey); pkey = vs->p15_containers[pAgreementInfo->bContainerIndex].prkey_obj; if (!pkey) { logprintf(pCardData, 2, "CardConstructDHAgreement prkey not found\n"); dwret = SCARD_E_NO_KEY_CONTAINER; goto err; } /* convert the Windows public key into an OpenSC public key */ publicKeySize = pAgreementInfo->dwPublicKey - sizeof(BCRYPT_ECCKEY_BLOB) + 1; pbPublicKey = (PBYTE) pCardData->pfnCspAlloc(publicKeySize); if (!pbPublicKey) { dwret = ERROR_OUTOFMEMORY; goto err; } pbPublicKey[0] = 4; 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, 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); dwret = md_translate_OpenSC_to_Windows_error(r, SCARD_E_INVALID_VALUE); goto err; } out = pCardData->pfnCspAlloc(outlen); if (!out) { dwret = ERROR_OUTOFMEMORY; goto err; } 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); if ( r < 0) { logprintf(pCardData, 2, "sc_pkcs15_derive error(%i): %s\n", r, sc_strerror(r)); pCardData->pfnCspFree(out); dwret = md_translate_OpenSC_to_Windows_error(r, SCARD_E_INVALID_VALUE); goto err; } /* save the dh agreement for later use */ /* try to find an empty index */ for (i = 0; i < vs->allocatedAgreements; i++) { dh_agreement = vs->dh_agreements + i; if (dh_agreement->pbAgreement == NULL) { pAgreementInfo->bSecretAgreementIndex = i; dh_agreement->pbAgreement = out; dh_agreement->dwSize = outlen; 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); dwret = SCARD_E_NO_MEMORY; goto err; } if ((vs->allocatedAgreements) > 0) { memcpy(temp, vs->dh_agreements, sizeof(struct md_dh_agreement) * (vs->allocatedAgreements)); pCardData->pfnCspFree(vs->dh_agreements); } vs->dh_agreements = temp; dh_agreement = vs->dh_agreements + (vs->allocatedAgreements); pAgreementInfo->bSecretAgreementIndex = (vs->allocatedAgreements); dh_agreement->pbAgreement = out; dh_agreement->dwSize = outlen; vs->allocatedAgreements++; 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 ) { DWORD dwReturn = 0; /* CNG variables */ BCRYPT_ALG_HANDLE hAlgorithm = NULL; BCRYPT_HASH_HANDLE hHash = NULL; DWORD dwSize, dwHashSize; PBYTE pbBuffer = NULL; DWORD dwBufferSize = 0; 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, "CardDeriveKey: unable to find a provider for the algorithm %S 0x%08X\n", szAlgorithm, (unsigned int)dwReturn); goto cleanup; } dwSize = sizeof(DWORD); dwReturn = BCryptGetProperty(hAlgorithm, BCRYPT_HASH_LENGTH, (PUCHAR)&dwHashSize, dwSize, &dwSize, 0); if (dwReturn) { logprintf(pCardData, 0, "CardDeriveKey: unable to get the hash length\n"); goto cleanup; } pAgreementInfo->cbDerivedKey = dwHashSize; if (pAgreementInfo->dwFlags & CARD_BUFFER_SIZE_ONLY) { dwReturn = SCARD_S_SUCCESS; goto cleanup; } pAgreementInfo->pbDerivedKey = (PBYTE)pCardData->pfnCspAlloc(dwHashSize); if (pAgreementInfo->pbDerivedKey == NULL) { dwReturn = SCARD_E_NO_MEMORY; goto cleanup; } dwSize = sizeof(DWORD); dwReturn = BCryptGetProperty(hAlgorithm, BCRYPT_OBJECT_LENGTH, (PUCHAR)&dwBufferSize, dwSize, &dwSize, 0); if (dwReturn) { logprintf(pCardData, 0, "CardDeriveKey: unable to get the buffer length 0x%08X\n", (unsigned int)dwReturn); goto cleanup; } pbBuffer = (PBYTE)LocalAlloc(0, dwBufferSize); if (pbBuffer == NULL) { dwReturn = SCARD_E_NO_MEMORY; goto cleanup; } if (wcscmp(pAgreementInfo->pwszKDF, BCRYPT_KDF_HMAC) == 0) { dwReturn = BCryptCreateHash(hAlgorithm, &hHash, pbBuffer, dwBufferSize, pbHmacKey, dwHmacKeySize, 0); } else { dwReturn = BCryptCreateHash(hAlgorithm, &hHash, pbBuffer, dwBufferSize, NULL, 0, 0); } if (dwReturn) { logprintf(pCardData, 0, "CardDeriveKey: unable to create the alg object 0x%08X\n", (unsigned int)dwReturn); goto cleanup; } parameters = (NCryptBufferDesc*) pAgreementInfo->pParameterList; if (parameters) { for (i = 0; i < parameters->cBuffers; i++) { NCryptBuffer* buffer = parameters->pBuffers + i; if (buffer->BufferType == KDF_SECRET_PREPEND) { dwReturn = BCryptHashData(hHash, (PUCHAR)buffer->pvBuffer, buffer->cbBuffer, 0); if (dwReturn) { logprintf(pCardData, 0, "CardDeriveKey: unable to hash data 0x%08X\n", (unsigned int)dwReturn); goto cleanup; } } } } dwReturn = BCryptHashData(hHash, (PUCHAR)agreement->pbAgreement, agreement->dwSize, 0); if (dwReturn) { logprintf(pCardData, 0, "CardDeriveKey: unable to hash data 0x%08X\n", (unsigned int)dwReturn); goto cleanup; } if (parameters) { for (i = 0; i < parameters->cBuffers; i++) { NCryptBuffer* buffer = parameters->pBuffers + i; if (buffer->BufferType == KDF_SECRET_APPEND) { dwReturn = BCryptHashData(hHash, (PUCHAR)buffer->pvBuffer, buffer->cbBuffer, 0); if (dwReturn) { logprintf(pCardData, 0, "CardDeriveKey: unable to hash data 0x%08X\n", (unsigned int)dwReturn); goto cleanup; } } } } dwReturn = BCryptFinishHash(hHash, pAgreementInfo->pbDerivedKey, pAgreementInfo->cbDerivedKey, 0); if (dwReturn) { logprintf(pCardData, 0, "CardDeriveKey: unable to finish hash 0x%08X\n", (unsigned int)dwReturn); goto cleanup; } cleanup: if (hHash) BCryptDestroyHash(hHash); if (pbBuffer) LocalFree(pbBuffer); if (hAlgorithm) BCryptCloseAlgorithmProvider(hAlgorithm, 0); 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, PBYTE pbData1, DWORD dwDataSize1, PBYTE pbData2, DWORD dwDataSize2, PBYTE pbData3, DWORD dwDataSize3 ) { DWORD dwReturn, dwSize, dwBufferSize; BCRYPT_HASH_HANDLE hHash = NULL; PBYTE pbBuffer = NULL; dwSize = sizeof(DWORD); dwReturn = BCryptGetProperty(hAlgorithm, BCRYPT_OBJECT_LENGTH, (PUCHAR)&dwBufferSize, dwSize, &dwSize, 0); if (dwReturn) { logprintf(pCardData, 0, "CardDeriveKey: unable to get the buffer length 0x%08X\n", (unsigned int)dwReturn); goto cleanup; } pbBuffer = (PBYTE)LocalAlloc(0, dwBufferSize); if (pbBuffer == NULL) { dwReturn = SCARD_E_NO_MEMORY; goto cleanup; } dwReturn = BCryptCreateHash(hAlgorithm, &hHash, pbBuffer, dwBufferSize, pbSecret, dwSecretSize, 0); if (dwReturn) { logprintf(pCardData, 0, "CardDeriveKey: unable to create the alg object 0x%08X\n", (unsigned int)dwReturn); goto cleanup; } if (pbData1) { dwReturn = BCryptHashData(hHash, pbData1, dwDataSize1, 0); if (dwReturn) { logprintf(pCardData, 0, "CardDeriveKey: unable to hash data 0x%08X\n", (unsigned int)dwReturn); goto cleanup; } } if (pbData2) { dwReturn = BCryptHashData(hHash, pbData2, dwDataSize2, 0); if (dwReturn) { logprintf(pCardData, 0, "CardDeriveKey: unable to hash data 0x%08X\n", (unsigned int)dwReturn); goto cleanup; } } if (pbData3) { dwReturn = BCryptHashData(hHash, pbData3, dwDataSize3, 0); if (dwReturn) { logprintf(pCardData, 0, "CardDeriveKey: unable to hash data 0x%08X\n", (unsigned int)dwReturn); goto cleanup; } } dwReturn = BCryptFinishHash(hHash, pbOuput, dwOutputSize, 0); if (dwReturn) { logprintf(pCardData, 0, "CardDeriveKey: unable to finish hash 0x%08X\n", (unsigned int)dwReturn); goto cleanup; } cleanup: if (hHash) BCryptDestroyHash(hHash); if (pbBuffer) LocalFree(pbBuffer); return dwReturn; } /* Generic function for TLS PRF. Compute the P_HASH function */ DWORD WINAPI DoTlsPrf(__in PCARD_DATA pCardData, __in PBYTE pbOutput, __in PBYTE pbSecret, __in DWORD dwSecretSize, __in PWSTR szAlgorithm, __in PBYTE pbLabel, __in DWORD dwLabelSize, __in PBYTE pbSeed ) { DWORD dwReturn = 0, i; /* CNG variables */ BCRYPT_ALG_HANDLE hAlgorithm = NULL; DWORD dwSize, dwHashSize, dwNumberOfRounds, dwLastRoundSize; PBYTE pbBuffer = NULL; /* TLS intermediate results */ PBYTE pbAx = NULL; dwReturn = BCryptOpenAlgorithmProvider(&hAlgorithm, szAlgorithm, NULL, BCRYPT_ALG_HANDLE_HMAC_FLAG); if (dwReturn) { logprintf(pCardData, 0, "CardDeriveKey: unable to find a provider for the algorithm %S 0x%08X\n", szAlgorithm, (unsigned int)dwReturn); goto cleanup; } dwSize = sizeof(DWORD); dwReturn = BCryptGetProperty(hAlgorithm, BCRYPT_HASH_LENGTH, (PUCHAR)&dwHashSize, dwSize, &dwSize, 0); if (dwReturn) { logprintf(pCardData, 0, "CardDeriveKey: unable to get the hash length\n"); goto cleanup; } /* size is always 48 */ dwLastRoundSize = TLS_DERIVE_KEY_SIZE % dwHashSize; if (dwLastRoundSize == 0) dwLastRoundSize = dwHashSize; dwNumberOfRounds = (DWORD) (TLS_DERIVE_KEY_SIZE / dwHashSize) + (dwLastRoundSize == dwHashSize?0:1); /* store TLS A1, A2 intermediate operations */ pbAx = (PBYTE) LocalAlloc(0, dwNumberOfRounds * dwHashSize); if (pbAx == NULL) { dwReturn = SCARD_E_NO_MEMORY; goto cleanup; } pbBuffer = (PBYTE) LocalAlloc(0, dwHashSize); if (pbBuffer == NULL) { dwReturn = SCARD_E_NO_MEMORY; goto cleanup; } for (i = 0; icbDerivedKey = TLS_DERIVE_KEY_SIZE; if (pAgreementInfo->dwFlags & CARD_BUFFER_SIZE_ONLY) { return SCARD_S_SUCCESS; } pAgreementInfo->pbDerivedKey = (PBYTE)pCardData->pfnCspAlloc(TLS_DERIVE_KEY_SIZE); if (pAgreementInfo->pbDerivedKey == NULL) { return SCARD_E_NO_MEMORY; } if (dwProtocol == TLS1_0_PROTOCOL_VERSION || dwProtocol == TLS1_1_PROTOCOL_VERSION) { /* TLS 1.0 & 1.1 */ DWORD dwNewSecretLength = (((agreement->dwSize) + (2) - 1) / (2)); dwReturn = DoTlsPrf(pCardData, pAgreementInfo->pbDerivedKey, agreement->pbAgreement, dwNewSecretLength, BCRYPT_MD5_ALGORITHM, pbLabel, dwLabelSize, pbSeed); if (dwReturn) { logprintf(pCardData, 0, "CardDeriveTlsPrf: unable to DoTlsPrf with %S 0x%08X\n", szAlgorithm, (unsigned int)dwReturn); pCardData->pfnCspFree(pAgreementInfo->pbDerivedKey ); pAgreementInfo->pbDerivedKey = NULL; return dwReturn; } pbBuffer = (PBYTE) LocalAlloc(0, TLS_DERIVE_KEY_SIZE); if (!pbBuffer) { pCardData->pfnCspFree(pAgreementInfo->pbDerivedKey ); pAgreementInfo->pbDerivedKey = NULL; return SCARD_E_NO_MEMORY; } dwReturn = DoTlsPrf(pCardData, pbBuffer, agreement->pbAgreement + dwNewSecretLength, dwNewSecretLength, BCRYPT_SHA1_ALGORITHM, pbLabel, dwLabelSize, pbSeed); if (dwReturn) { logprintf(pCardData, 0, "CardDeriveTlsPrf: unable to DoTlsPrf with %S 0x%08X\n", szAlgorithm, (unsigned int)dwReturn); LocalFree(pbBuffer); pCardData->pfnCspFree(pAgreementInfo->pbDerivedKey ); pAgreementInfo->pbDerivedKey = NULL; return dwReturn; } for (i = 0; i< TLS_DERIVE_KEY_SIZE; i++) { pAgreementInfo->pbDerivedKey[i] = pAgreementInfo->pbDerivedKey[i] ^ pbBuffer[i]; } LocalFree(pbBuffer); } else if (dwProtocol == TLS1_2_PROTOCOL_VERSION) { dwReturn = DoTlsPrf(pCardData, pAgreementInfo->pbDerivedKey, agreement->pbAgreement, agreement->dwSize, szAlgorithm, pbLabel, dwLabelSize, pbSeed); if (dwReturn) { logprintf(pCardData, 0, "CardDeriveTlsPrf: unable to DoTlsPrf with %S 0x%08X\n", szAlgorithm, (unsigned int)dwReturn); pCardData->pfnCspFree(pAgreementInfo->pbDerivedKey ); pAgreementInfo->pbDerivedKey = NULL; return dwReturn; } } return SCARD_S_SUCCESS; } DWORD WINAPI CardDeriveKey(__in PCARD_DATA pCardData, __inout PCARD_DERIVE_KEY pAgreementInfo) { VENDOR_SPECIFIC *vs; struct md_dh_agreement* agreement = NULL; NCryptBufferDesc* parameters = NULL; ULONG i; DWORD dwReturn = 0; /* store parameter references */ PWSTR szAlgorithm = NULL; PBYTE pbHmacKey = NULL; DWORD dwHmacKeySize = 0; PBYTE pbLabel = NULL; DWORD dwLabelSize = 0; PBYTE pbSeed = NULL; DWORD dwProtocol = 0; logprintf(pCardData, 1, "\nP:%lu T:%lu pCardData:%p ", (unsigned long)GetCurrentProcessId(), (unsigned long)GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardDeriveKey\n"); MD_FUNC_CALLED(pCardData, 1); if (!pCardData) MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); if (!pAgreementInfo) MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); if (!pAgreementInfo->dwVersion) MD_FUNC_RETURN(pCardData, 1, ERROR_REVISION_MISMATCH); if (pAgreementInfo->dwVersion > CARD_DERIVE_KEY_CURRENT_VERSION) MD_FUNC_RETURN(pCardData, 1, ERROR_REVISION_MISMATCH); if (pAgreementInfo->pwszKDF == NULL) 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)) MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); /* 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) { MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); } agreement = vs->dh_agreements + pAgreementInfo->bSecretAgreementIndex; if (agreement->pbAgreement == NULL) { MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); } if (pAgreementInfo->dwFlags & CARD_RETURN_KEY_HANDLE ) { 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; switch(buffer->BufferType) { case KDF_HASH_ALGORITHM: if (szAlgorithm != NULL) { logprintf(pCardData, 0, "CardDeriveKey: got more than one algorithm\n"); return SCARD_E_INVALID_PARAMETER; } if (wcscmp((PWSTR) buffer->pvBuffer, BCRYPT_SHA1_ALGORITHM) == 0) { szAlgorithm = BCRYPT_SHA1_ALGORITHM; } else if (wcscmp((PWSTR) buffer->pvBuffer, BCRYPT_SHA256_ALGORITHM) == 0) { szAlgorithm = BCRYPT_SHA256_ALGORITHM; } else if (wcscmp((PWSTR) buffer->pvBuffer, BCRYPT_SHA384_ALGORITHM) == 0) { szAlgorithm = BCRYPT_SHA384_ALGORITHM; } else if (wcscmp((PWSTR) buffer->pvBuffer, BCRYPT_SHA512_ALGORITHM) == 0) { szAlgorithm = BCRYPT_SHA512_ALGORITHM; } else if (wcscmp((PWSTR) buffer->pvBuffer, BCRYPT_MD5_ALGORITHM) == 0) { szAlgorithm = BCRYPT_MD5_ALGORITHM; } else { logprintf(pCardData, 0, "CardDeriveKey: unsupported algorithm %S\n", (PWSTR)buffer->pvBuffer); MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); } break; case KDF_HMAC_KEY: if (pbHmacKey != NULL) { logprintf(pCardData, 0, "CardDeriveKey: got more than one hhmac key\n"); return SCARD_E_INVALID_PARAMETER; } pbHmacKey = (PBYTE) buffer->pvBuffer; dwHmacKeySize = buffer->cbBuffer; break; case KDF_SECRET_APPEND: case KDF_SECRET_PREPEND: /* do not throw an error for invalid arg*/ break; case KDF_TLS_PRF_LABEL: if (pbLabel != NULL) { logprintf(pCardData, 0, "CardDeriveKey: got more than one Label\n"); MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); } pbLabel = (PBYTE)buffer->pvBuffer; dwLabelSize = buffer->cbBuffer; break; case KDF_TLS_PRF_SEED: if (pbSeed != NULL) { logprintf(pCardData, 0, "CardDeriveKey: got more than one Seed\n"); MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); } if (buffer->cbBuffer != 64) { logprintf(pCardData, 0, "CardDeriveKey: invalid seed size %lu\n", buffer->cbBuffer); MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); } pbSeed = (PBYTE)buffer->pvBuffer; break; case KDF_TLS_PRF_PROTOCOL: dwProtocol = *((PDWORD)buffer->pvBuffer); break; /*case KDF_ALGORITHMID: case KDF_PARTYUINFO: case KDF_PARTYVINFO: case KDF_SUPPPUBINFO: case KDF_SUPPPRIVINFO: break;*/ default: logprintf(pCardData, 0, "CardDeriveKey: unknown buffer type %lu\n", (parameters->pBuffers + i)->BufferType); MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); } } } /* default parameters */ if (szAlgorithm == NULL && wcscmp(pAgreementInfo->pwszKDF, BCRYPT_KDF_TLS_PRF) != 0) { szAlgorithm = BCRYPT_SHA1_ALGORITHM; } /* 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"); 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"); MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); } if (!pbLabel) { logprintf(pCardData, 0, "CardDeriveKey: No label was provided\n"); MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); } } else { logprintf(pCardData, 0, "CardDeriveKey: unsupported KDF %S\n", pAgreementInfo->pwszKDF); MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); } /* do the job for the KDF Hash & Hmac */ if (wcscmp(pAgreementInfo->pwszKDF, BCRYPT_KDF_HASH) == 0 || wcscmp(pAgreementInfo->pwszKDF, BCRYPT_KDF_HMAC) == 0 ) { dwReturn = CardDeriveHashOrHMAC(pCardData, pAgreementInfo, agreement, szAlgorithm, pbHmacKey, dwHmacKeySize); if (dwReturn) { logprintf(pCardData, 0, "CardDeriveKey: got an error while deriving the Key (hash or HMAC) 0x%08X\n", (unsigned int)dwReturn); MD_FUNC_RETURN(pCardData, 1, dwReturn); } } else if (wcscmp(pAgreementInfo->pwszKDF, BCRYPT_KDF_TLS_PRF) == 0) { dwReturn = CardDeriveTlsPrf(pCardData, pAgreementInfo, agreement, dwProtocol, szAlgorithm, pbLabel, dwLabelSize, pbSeed); if (dwReturn) { logprintf(pCardData, 0, "CardDeriveKey: got an error while deriving the Key (TlsPrf) 0x%08X\n", (unsigned int)dwReturn); MD_FUNC_RETURN(pCardData, 1, dwReturn); } } /*else if (wcscmp(pAgreementInfo->pwszKDF, BCRYPT_KDF_SP80056A_CONCAT ) == 0) { }*/ MD_FUNC_RETURN(pCardData, 1, SCARD_S_SUCCESS); } DWORD WINAPI CardDestroyDHAgreement( __in PCARD_DATA pCardData, __in BYTE bSecretAgreementIndex, __in DWORD dwFlags) { 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) MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); if (dwFlags) 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) { MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); } agreement = vs->dh_agreements + bSecretAgreementIndex; if (agreement->pbAgreement == NULL) { MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); } SecureZeroMemory(agreement->pbAgreement, agreement->dwSize); pCardData->pfnCspFree(agreement->pbAgreement); agreement->pbAgreement = 0; agreement->dwSize = 0; MD_FUNC_RETURN(pCardData, 1, SCARD_S_SUCCESS); } DWORD WINAPI CardGetChallengeEx(__in PCARD_DATA pCardData, __in PIN_ID PinId, __deref_out_bcount(*pcbChallengeData) PBYTE *ppbChallengeData, __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"); MD_FUNC_RETURN(pCardData, 1, SCARD_E_UNSUPPORTED_FEATURE); } DWORD WINAPI CardAuthenticateEx(__in PCARD_DATA pCardData, __in PIN_ID PinId, __in DWORD dwFlags, __in_bcount(cbPinData) PBYTE pbPinData, __in DWORD cbPinData, __deref_opt_out_bcount(*pcbSessionPin) PBYTE *ppbSessionPin, __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; unsigned int auth_method; 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) 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", (unsigned int)PinId, (unsigned int)dwFlags, (unsigned long)cbPinData, pcAttemptsRemaining ? "YES" : "NO"); vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); if (!vs) MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); if (PinId >= MD_MAX_PINS) MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); 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 * doesn't support it or if the minidriver doesn't support it in general? * */ 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)) 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)) 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)) 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(pcAttemptsRemaining) (*pcAttemptsRemaining) = (DWORD) -1; auth_info = (struct sc_pkcs15_auth_info *)pin_obj->data; /* save the pin type */ auth_method = auth_info->auth_method; /* Do we need to display a prompt to enter PIN on pin pad? */ logprintf(pCardData, 7, "PIN pad=%s, pbPinData=%p, hwndParent=%p\n", vs->reader->capabilities & SC_READER_CAP_PIN_PAD || vs->p15card->card->caps & SC_CARD_CAP_PROTECTED_AUTHENTICATION_PATH ? "yes" : "no", pbPinData, vs->hwndParent); if (dwFlags & CARD_AUTHENTICATE_SESSION_PIN) { /* check if the pin is the session pin generated by a previous authentication with a pinpad */ if (pbPinData != NULL && cbPinData == sizeof(MAGIC_SESSION_PIN) && memcmp(MAGIC_SESSION_PIN, pbPinData, sizeof(MAGIC_SESSION_PIN)) == 0) { logprintf(pCardData, 2, "use magic session pin"); pbPinData = NULL; cbPinData = 0; } else { /* seems we have a real session pin, set the pin type accordingly */ 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"); *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 != NULL) { pCardData->pfnCspFree(*ppbSessionPin); *ppbSessionPin = NULL; } *pcbSessionPin = 0; logprintf(pCardData, 2, "generating session pin failed"); } else { 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 != NULL) { pCardData->pfnCspFree(*ppbSessionPin); *ppbSessionPin = NULL; } *pcbSessionPin = 0; } } } else { if (pcbSessionPin) *pcbSessionPin = 0; if (ppbSessionPin) *ppbSessionPin = NULL; logprintf(pCardData, 2, "standard pin verification"); /* * 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 */ auth_info->auth_method = auth_method; if (r) { logprintf(pCardData, 1, "PIN code verification failed: %s; tries left %i\n", sc_strerror(r), auth_info->tries_left); if (r == SC_ERROR_AUTH_METHOD_BLOCKED) { if(pcAttemptsRemaining) (*pcAttemptsRemaining) = 0; MD_FUNC_RETURN(pCardData, 1, SCARD_W_CHV_BLOCKED); } if(pcAttemptsRemaining) (*pcAttemptsRemaining) = auth_info->tries_left; 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 == 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 */ *ppbSessionPin = pCardData->pfnCspAlloc(sizeof(MAGIC_SESSION_PIN)); if (*ppbSessionPin != NULL) { memcpy(*ppbSessionPin, MAGIC_SESSION_PIN, sizeof(MAGIC_SESSION_PIN)); *pcbSessionPin = sizeof(MAGIC_SESSION_PIN); } } MD_FUNC_RETURN(pCardData, 1, SCARD_S_SUCCESS); } DWORD WINAPI CardChangeAuthenticatorEx(__in PCARD_DATA pCardData, __in DWORD dwFlags, __in PIN_ID dwAuthenticatingPinId, __in_bcount(cbAuthenticatingPinData) PBYTE pbAuthenticatingPinData, __in DWORD cbAuthenticatingPinData, __in PIN_ID dwTargetPinId, __in_bcount(cbTargetData) PBYTE pbTargetData, __in DWORD cbTargetData, __in DWORD cRetryCount, __out_opt PDWORD pcAttemptsRemaining) { DWORD dwret; VENDOR_SPECIFIC *vs = NULL; 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(), (unsigned long)GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardChangeAuthenticatorEx\n"); if (!pCardData) 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"); MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); } if ((dwFlags & PIN_CHANGE_FLAG_UNBLOCK) && (dwFlags & PIN_CHANGE_FLAG_CHANGEPIN)) MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); if (dwFlags & PIN_CHANGE_FLAG_UNBLOCK && dwAuthenticatingPinId == dwTargetPinId) 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) 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", (unsigned int)dwAuthenticatingPinId, (unsigned int)dwFlags, (unsigned long)cbAuthenticatingPinData, (unsigned int)dwTargetPinId, (unsigned long)cbTargetData, pcAttemptsRemaining ? "YES" : "NO"); 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"); MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); } if (pbTargetData == NULL || cbTargetData == 0) { logprintf(pCardData, 1, "Invalid new PIN data\n"); MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); } } /* using a pin pad */ if (NULL == pbAuthenticatingPinData) { if (!(dwFlags & CARD_PIN_SILENT_CONTEXT) && !(vs->ctx->flags & SC_CTX_FLAG_DISABLE_POPUPS)) { DisplayPinpadUI = TRUE; } } pin_obj = vs->pin_objs[dwTargetPinId]; if(pcAttemptsRemaining) (*pcAttemptsRemaining) = (DWORD) -1; /* 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"), (dwTargetPinId==ROLE_ADMIN?"admin":"user"), sc_strerror(rv), rv); auth_info = (struct sc_pkcs15_auth_info *)pin_obj->data; if (rv == SC_ERROR_AUTH_METHOD_BLOCKED) { if(pcAttemptsRemaining) (*pcAttemptsRemaining) = 0; MD_FUNC_RETURN(pCardData, 1, SCARD_W_CHV_BLOCKED); } if(pcAttemptsRemaining) (*pcAttemptsRemaining) = auth_info->tries_left; MD_FUNC_RETURN(pCardData, 1, md_translate_OpenSC_to_Windows_error(rv, SCARD_W_WRONG_CHV)); } logprintf(pCardData, 7, "returns success\n"); 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(), (unsigned long)GetCurrentThreadId(), pCardData); logprintf(pCardData, 1, "CardDeauthenticateEx PinId=%u dwFlags=0x%08X\n", (unsigned int)PinId, (unsigned int)dwFlags); MD_FUNC_RETURN(pCardData, 1, CardDeauthenticate(pCardData, wszCARD_USER_USER, 0)); } DWORD WINAPI CardGetContainerProperty(__in PCARD_DATA pCardData, __in BYTE bContainerIndex, __in LPCWSTR wszProperty, __out_bcount_part_opt(cbData, *pdwDataLen) PBYTE pbData, __in DWORD cbData, __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"); 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); 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) MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); if (dwFlags) MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); if (!pbData || !pdwDataLen) MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); if (bContainerIndex >= MD_MAX_KEY_CONTAINERS) MD_FUNC_RETURN(pCardData, 1, SCARD_E_NO_KEY_CONTAINER); /* 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); MD_FUNC_RETURN(pCardData, 1, SCARD_E_NO_KEY_CONTAINER); } if (wcscmp(CCP_CONTAINER_INFO,wszProperty) == 0) { PCONTAINER_INFO p = (PCONTAINER_INFO) pbData; if (pdwDataLen) *pdwDataLen = sizeof(*p); if (cbData >= sizeof(DWORD)) if (p->dwVersion != CONTAINER_INFO_CURRENT_VERSION && p->dwVersion != 0 ) MD_FUNC_RETURN(pCardData, 1, ERROR_REVISION_MISMATCH); if (cbData < sizeof(*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) { PPIN_ID p = (PPIN_ID) pbData; if (pdwDataLen) *pdwDataLen = sizeof(*p); if (cbData < sizeof(*p)) 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); MD_FUNC_RETURN(pCardData, 1, SCARD_S_SUCCESS); } MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); } DWORD WINAPI CardSetContainerProperty(__in PCARD_DATA pCardData, __in BYTE bContainerIndex, __in LPCWSTR wszProperty, __in_bcount(cbDataLen) PBYTE pbData, __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"); MD_FUNC_RETURN(pCardData, 1, SCARD_E_UNSUPPORTED_FEATURE); } DWORD WINAPI CardGetProperty(__in PCARD_DATA pCardData, __in LPCWSTR wszProperty, __out_bcount_part_opt(cbData, *pdwDataLen) PBYTE pbData, __in DWORD cbData, __out PDWORD pdwDataLen, __in DWORD dwFlags) { 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); logprintf(pCardData, 2, "CardGetProperty('%S',cbData=%lu,dwFlags=%lu) called\n", NULLWSTR(wszProperty), (unsigned long)cbData, (unsigned long)dwFlags); if (!pCardData || !wszProperty) MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); if (!pbData || !pdwDataLen) MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); dwret = check_card_reader_status(pCardData, "CardGetProperty"); 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 (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)) 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"); MD_FUNC_RETURN(pCardData, 1, dwret); } } else if (wcscmp(CP_CARD_CAPABILITIES, wszProperty) == 0) { PCARD_CAPABILITIES pCardCapabilities = (PCARD_CAPABILITIES )pbData; if (pdwDataLen) *pdwDataLen = sizeof(*pCardCapabilities); if (cbData < sizeof(*pCardCapabilities)) MD_FUNC_RETURN(pCardData, 1, ERROR_INSUFFICIENT_BUFFER); dwret = md_card_capabilities(pCardData, pCardCapabilities); if (dwret != SCARD_S_SUCCESS) 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)) MD_FUNC_RETURN(pCardData, 1, ERROR_INSUFFICIENT_BUFFER); /* dwFlags has key_type */ dwret = md_query_key_sizes(pCardData, dwFlags, pKeySizes); if (dwret != SCARD_S_SUCCESS) 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)) MD_FUNC_RETURN(pCardData, 1, ERROR_INSUFFICIENT_BUFFER); *p = md_is_read_only(pCardData); } else if (wcscmp(CP_CARD_CACHE_MODE, wszProperty) == 0) { DWORD *p = (DWORD *)pbData; if (pdwDataLen) *pdwDataLen = sizeof(*p); if (cbData < sizeof(*p)) MD_FUNC_RETURN(pCardData, 1, ERROR_INSUFFICIENT_BUFFER); *p = CP_CACHE_MODE_NO_CACHE; } else if (wcscmp(CP_SUPPORTS_WIN_X509_ENROLLMENT, wszProperty) == 0) { BOOL *p = (BOOL *)pbData; if (pdwDataLen) *pdwDataLen = sizeof(*p); if (cbData < sizeof(*p)) MD_FUNC_RETURN(pCardData, 1, ERROR_INSUFFICIENT_BUFFER); *p = md_is_supports_X509_enrollment(pCardData); } else if (wcscmp(CP_CARD_GUID, wszProperty) == 0) { struct md_file *cardid = NULL; md_fs_find_file(pCardData, NULL, "cardid", &cardid); if (!cardid) { logprintf(pCardData, 2, "file 'cardid' not found\n"); MD_FUNC_RETURN(pCardData, 1, SCARD_E_FILE_NOT_FOUND); } if (pdwDataLen) *pdwDataLen = (DWORD) cardid->size; if (cbData < cardid->size) MD_FUNC_RETURN(pCardData, 1, ERROR_INSUFFICIENT_BUFFER); CopyMemory(pbData, cardid->blob, cardid->size); } else if (wcscmp(CP_CARD_SERIAL_NO, wszProperty) == 0) { unsigned char buf[64]; size_t buf_len = sizeof(buf); if (sc_hex_to_bin(vs->p15card->tokeninfo->serial_number, buf, &buf_len)) { buf_len = strlen(vs->p15card->tokeninfo->serial_number); if (buf_len > SC_MAX_SERIALNR) { buf_len = SC_MAX_SERIALNR; } memcpy(buf, vs->p15card->tokeninfo->serial_number, buf_len); } if (pdwDataLen) *pdwDataLen = (DWORD) buf_len; if (cbData < buf_len) MD_FUNC_RETURN(pCardData, 1, ERROR_INSUFFICIENT_BUFFER); CopyMemory(pbData, buf, buf_len); } else if (wcscmp(CP_CARD_PIN_INFO, wszProperty) == 0) { PPIN_INFO p = (PPIN_INFO) pbData; if (pdwDataLen) *pdwDataLen = sizeof(*p); if (cbData < sizeof(*p)) MD_FUNC_RETURN(pCardData, 1, ERROR_INSUFFICIENT_BUFFER); if (p->dwVersion != PIN_INFO_CURRENT_VERSION) 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_EVERYONE: logprintf(pCardData, 2, "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->dwChangePermission = 0; break; case ROLE_ADMIN: logprintf(pCardData, 2, "returning info on PIN ROLE_ADMIN ( Unblock ) [%lu]\n", (unsigned long)dwFlags); p->PinPurpose = UnblockOnlyPin; p->PinCachePolicy.dwVersion = PIN_CACHE_POLICY_CURRENT_VERSION; p->PinCachePolicy.dwPinCachePolicyInfo = 0; p->PinCachePolicy.PinCachePolicyType = PinCacheNormal; p->dwChangePermission = CREATE_PIN_SET(ROLE_ADMIN); p->dwUnblockPermission = 0; break; default: logprintf(pCardData, 2, "returning info on normal PIN [%lu]\n", (unsigned long)dwFlags); 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)) 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)) MD_FUNC_RETURN(pCardData, 1, ERROR_INSUFFICIENT_BUFFER); logprintf(pCardData, 7, "CARD_AUTHENTICATED_STATE invalid\n"); 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 >= 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)) 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; } } else if (wcscmp(CP_KEY_IMPORT_SUPPORT, wszProperty) == 0) { DWORD *p = (DWORD *)pbData; if (pdwDataLen) *pdwDataLen = sizeof(*p); if (cbData < sizeof(*p)) 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 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 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 MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); } else { logprintf(pCardData, 3, "Unsupported property '%S'\n", wszProperty); MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); } logprintf(pCardData, 7, "returns '%S' ", wszProperty); loghex(pCardData, 7, pbData, *pdwDataLen); MD_FUNC_RETURN(pCardData, 1, SCARD_S_SUCCESS); } DWORD WINAPI CardSetProperty(__in PCARD_DATA pCardData, __in LPCWSTR wszProperty, __in_bcount(cbDataLen) PBYTE pbData, __in DWORD cbDataLen, __in DWORD dwFlags) { 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) MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); logprintf(pCardData, 2, "CardSetProperty wszProperty=%S, pbData=%p, cbDataLen=%lu, dwFlags=%lu", NULLWSTR(wszProperty), pbData, (unsigned long)cbDataLen, (unsigned long)dwFlags); vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); if (!vs) MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); if (!wszProperty) MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); if (dwFlags) MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); if (!cbDataLen) 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 || wcscmp(wszProperty,CP_CARD_CAPABILITIES) == 0 || wcscmp(wszProperty,CP_CARD_KEYSIZES) == 0 || wcscmp(wszProperty,CP_CARD_LIST_PINS) == 0 || wcscmp(wszProperty,CP_CARD_AUTHENTICATED_STATE) == 0 || wcscmp(wszProperty,CP_KEY_IMPORT_SUPPORT) == 0 || wcscmp(wszProperty,CP_ENUM_ALGORITHMS) == 0 || wcscmp(wszProperty,CP_PADDING_SCHEMES) == 0 || wcscmp(wszProperty,CP_CHAINING_MODES) == 0 || wcscmp(wszProperty,CP_SUPPORTS_WIN_X509_ENROLLMENT) == 0 || wcscmp(wszProperty,CP_CARD_CACHE_MODE) == 0 || wcscmp(wszProperty,CP_CARD_SERIAL_NO) == 0 ) { 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 ) { MD_FUNC_RETURN(pCardData, 1, SCARD_E_UNSUPPORTED_FEATURE); } /* This property and CP_PIN_CONTEXT_STRING are set just prior to a call to * CardAuthenticateEx if the PIN required is declared of type ExternalPinType. */ if (wcscmp(CP_PARENT_WINDOW, wszProperty) == 0) { if (cbDataLen != sizeof(HWND) || !pbData) { MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); } else { HWND cp = *((HWND *) pbData); if (cp!=0 && !IsWindow(cp)) MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); vs->hwndParent = cp; } logprintf(pCardData, 3, "Saved parent window (%p)\n", vs->hwndParent); 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); MD_FUNC_RETURN(pCardData, 1, SCARD_S_SUCCESS); } logprintf(pCardData, 3, "INVALID PARAMETER\n"); MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); } // 4.8 Secure key injection /** The CardImportSessionKey function imports a temporary session key to the card. The session key is encrypted with a key exchange key, and the function returns a handle of the imported session key to the caller.*/ DWORD WINAPI CardImportSessionKey( __in PCARD_DATA pCardData, __in BYTE bContainerIndex, __in VOID *pPaddingInfo, __in LPCWSTR pwszBlobType, __in LPCWSTR pwszAlgId, __out CARD_KEY_HANDLE *phKey, __in_bcount(cbInput) PBYTE pbInput, __in DWORD cbInput, __in DWORD dwFlags ) { UNREFERENCED_PARAMETER(pCardData); UNREFERENCED_PARAMETER(bContainerIndex); UNREFERENCED_PARAMETER(pCardData); UNREFERENCED_PARAMETER(pPaddingInfo); UNREFERENCED_PARAMETER(pwszBlobType); UNREFERENCED_PARAMETER(pwszAlgId); UNREFERENCED_PARAMETER(phKey); 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"); MD_FUNC_RETURN(pCardData, 1, SCARD_E_UNSUPPORTED_FEATURE); } /** The MDImportSessionKey function imports a temporary session key to the card minidriver and returns a key handle to the caller.*/ DWORD WINAPI MDImportSessionKey( __in PCARD_DATA pCardData, __in LPCWSTR pwszBlobType, __in LPCWSTR pwszAlgId, __out PCARD_KEY_HANDLE phKey, __in_bcount(cbInput) PBYTE pbInput, __in DWORD cbInput ) { UNREFERENCED_PARAMETER(pCardData); UNREFERENCED_PARAMETER(pwszBlobType); UNREFERENCED_PARAMETER(pwszAlgId); 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"); MD_FUNC_RETURN(pCardData, 1, SCARD_E_UNSUPPORTED_FEATURE); } /** The MDEncryptData function uses a key handle to encrypt data with a symmetric key. The data is encrypted in a format that the smart card supports.*/ DWORD WINAPI MDEncryptData( __in PCARD_DATA pCardData, __in CARD_KEY_HANDLE hKey, __in LPCWSTR pwszSecureFunction, __in_bcount(cbInput) PBYTE pbInput, __in DWORD cbInput, __in DWORD dwFlags, __deref_out_ecount(*pcEncryptedData) PCARD_ENCRYPTED_DATA *ppEncryptedData, __out PDWORD pcEncryptedData ) { UNREFERENCED_PARAMETER(pCardData); UNREFERENCED_PARAMETER(hKey); UNREFERENCED_PARAMETER(pwszSecureFunction); UNREFERENCED_PARAMETER(pbInput); UNREFERENCED_PARAMETER(cbInput); 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"); MD_FUNC_RETURN(pCardData, 1, SCARD_E_UNSUPPORTED_FEATURE); } /** The CardGetSharedKeyHandle function returns a session key handle to the caller. Note: The manner in which this session key has been established is outside the scope of this specification. For example, the session key could be established by either a permanent shared key or a key derivation algorithm that has occurred before the call to CardGetSharedKeyHandle.*/ DWORD WINAPI CardGetSharedKeyHandle( __in PCARD_DATA pCardData, __in_bcount(cbInput) PBYTE pbInput, __in DWORD cbInput, __deref_opt_out_bcount(*pcbOutput) PBYTE *ppbOutput, __out_opt PDWORD pcbOutput, __out PCARD_KEY_HANDLE phKey ) { UNREFERENCED_PARAMETER(pCardData); UNREFERENCED_PARAMETER(pbInput); UNREFERENCED_PARAMETER(cbInput); 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"); MD_FUNC_RETURN(pCardData, 1, SCARD_E_UNSUPPORTED_FEATURE); } /** The CardDestroyKey function releases a temporary key on the card. The card should delete all of the key material that is associated with that key handle.*/ DWORD WINAPI CardDestroyKey( __in PCARD_DATA pCardData, __in CARD_KEY_HANDLE hKey ) { 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"); MD_FUNC_RETURN(pCardData, 1, SCARD_E_UNSUPPORTED_FEATURE); } /** This function can be used to get properties for a cryptographic algorithm.*/ DWORD WINAPI CardGetAlgorithmProperty ( __in PCARD_DATA pCardData, __in LPCWSTR pwszAlgId, __in LPCWSTR pwszProperty, __out_bcount_part_opt(cbData, *pdwDataLen) PBYTE pbData, __in DWORD cbData, __out PDWORD pdwDataLen, __in DWORD dwFlags ) { UNREFERENCED_PARAMETER(pCardData); UNREFERENCED_PARAMETER(pwszAlgId); UNREFERENCED_PARAMETER(pwszProperty); UNREFERENCED_PARAMETER(pbData); 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"); MD_FUNC_RETURN(pCardData, 1, SCARD_E_UNSUPPORTED_FEATURE); } /** This function is used to get the properties of a key.*/ DWORD WINAPI CardGetKeyProperty( __in PCARD_DATA pCardData, __in CARD_KEY_HANDLE hKey, __in LPCWSTR pwszProperty, __out_bcount_part_opt(cbData, *pdwDataLen) PBYTE pbData, __in DWORD cbData, __out PDWORD pdwDataLen, __in DWORD dwFlags ) { UNREFERENCED_PARAMETER(pCardData); UNREFERENCED_PARAMETER(hKey); UNREFERENCED_PARAMETER(pwszProperty); UNREFERENCED_PARAMETER(pbData); 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"); MD_FUNC_RETURN(pCardData, 1, SCARD_E_UNSUPPORTED_FEATURE); } /** This function is used to set the properties of a key.*/ DWORD WINAPI CardSetKeyProperty( __in PCARD_DATA pCardData, __in CARD_KEY_HANDLE hKey, __in LPCWSTR pwszProperty, __in_bcount(cbInput) PBYTE pbInput, __in DWORD cbInput, __in DWORD dwFlags ) { UNREFERENCED_PARAMETER(pCardData); UNREFERENCED_PARAMETER(dwFlags); UNREFERENCED_PARAMETER(hKey); UNREFERENCED_PARAMETER(pwszProperty); 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"); MD_FUNC_RETURN(pCardData, 1, SCARD_E_UNSUPPORTED_FEATURE); } /** CardProcessEncryptedData processes a set of encrypted data BLOBs by sending them to the card where the data BLOBs are decrypted.*/ DWORD WINAPI CardProcessEncryptedData( __in PCARD_DATA pCardData, __in CARD_KEY_HANDLE hKey, __in LPCWSTR pwszSecureFunction, __in_ecount(cEncryptedData) PCARD_ENCRYPTED_DATA pEncryptedData, __in DWORD cEncryptedData, __out_bcount_part_opt(cbOutput, *pdwOutputLen) PBYTE pbOutput, __in DWORD cbOutput, __out_opt PDWORD pdwOutputLen, __in DWORD dwFlags ) { UNREFERENCED_PARAMETER(pCardData); UNREFERENCED_PARAMETER(hKey); UNREFERENCED_PARAMETER(pwszSecureFunction); UNREFERENCED_PARAMETER(pEncryptedData); UNREFERENCED_PARAMETER(cEncryptedData); UNREFERENCED_PARAMETER(pbOutput); 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"); 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) 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, "Invalid handle.\n"); MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_HANDLE); } if( pCardData->hScard == 0) { logprintf(pCardData, 0, "Invalid handle.\n"); MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_HANDLE); } } else { /* secure key injection not supported */ MD_FUNC_RETURN(pCardData, 1, SCARD_E_UNSUPPORTED_FEATURE); } if (pCardData->pbAtr == NULL) MD_FUNC_RETURN(pCardData, 1, SCARD_E_INVALID_PARAMETER); if ( pCardData->pwszCardName == NULL ) 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) MD_FUNC_RETURN(pCardData, 1, SCARD_E_UNKNOWN_CARD); /* Memory management functions */ if ( ( pCardData->pfnCspAlloc == NULL ) || ( pCardData->pfnCspReAlloc == NULL ) || ( pCardData->pfnCspFree == NULL ) ) 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) MD_FUNC_RETURN(pCardData, 1, (DWORD) ERROR_REVISION_MISMATCH); suppliedVersion = pCardData->dwVersion; /* VENDOR SPECIFIC */ vs = pCardData->pvVendorSpecific = pCardData->pfnCspAlloc(sizeof(VENDOR_SPECIFIC)); if (!vs) 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%08"SC_FORMAT_LEN_SIZE_T"X, hSCardCtx=0x%08"SC_FORMAT_LEN_SIZE_T"X\n", (unsigned long)pCardData->dwVersion, NULLWSTR(pCardData->pwszCardName), (size_t)pCardData->hScard, (size_t)pCardData->hSCardCtx); vs->hScard = pCardData->hScard; vs->hSCardCtx = pCardData->hSCardCtx; logprintf(pCardData, 2, "request version pCardData->dwVersion = %lu\n", (unsigned long)pCardData->dwVersion); pCardData->dwVersion = min(pCardData->dwVersion, MD_CURRENT_VERSION_SUPPORTED); logprintf(pCardData, 2, "pCardData->dwVersion = %lu\n", (unsigned long)pCardData->dwVersion); dwret = md_create_context(pCardData, vs); if (dwret != SCARD_S_SUCCESS) goto ret_free; pCardData->pfnCardDeleteContext = CardDeleteContext; pCardData->pfnCardQueryCapabilities = CardQueryCapabilities; pCardData->pfnCardDeleteContainer = CardDeleteContainer; pCardData->pfnCardCreateContainer = CardCreateContainer; pCardData->pfnCardGetContainerInfo = CardGetContainerInfo; pCardData->pfnCardAuthenticatePin = CardAuthenticatePin; pCardData->pfnCardGetChallenge = CardGetChallenge; pCardData->pfnCardAuthenticateChallenge = CardAuthenticateChallenge; pCardData->pfnCardUnblockPin = CardUnblockPin; pCardData->pfnCardChangeAuthenticator = CardChangeAuthenticator; pCardData->pfnCardDeauthenticate = CardDeauthenticate; pCardData->pfnCardCreateDirectory = CardCreateDirectory; pCardData->pfnCardDeleteDirectory = CardDeleteDirectory; pCardData->pvUnused3 = NULL; pCardData->pvUnused4 = NULL; pCardData->pfnCardCreateFile = CardCreateFile; pCardData->pfnCardReadFile = CardReadFile; pCardData->pfnCardWriteFile = CardWriteFile; pCardData->pfnCardDeleteFile = CardDeleteFile; pCardData->pfnCardEnumFiles = CardEnumFiles; pCardData->pfnCardGetFileInfo = CardGetFileInfo; pCardData->pfnCardQueryFreeSpace = CardQueryFreeSpace; pCardData->pfnCardQueryKeySizes = CardQueryKeySizes; pCardData->pfnCardSignData = CardSignData; pCardData->pfnCardRSADecrypt = CardRSADecrypt; pCardData->pfnCardConstructDHAgreement = CardConstructDHAgreement; dwret = associate_card(pCardData); if (dwret != SCARD_S_SUCCESS) goto ret_release; dwret = md_fs_init(pCardData); 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", (unsigned long)suppliedVersion, (unsigned long)pCardData->dwVersion); if (pCardData->dwVersion >= CARD_DATA_VERSION_FIVE) { pCardData->pfnCardDeriveKey = CardDeriveKey; pCardData->pfnCardDestroyDHAgreement = CardDestroyDHAgreement; if (pCardData->dwVersion >= CARD_DATA_VERSION_SIX) { pCardData->pfnCardGetChallengeEx = CardGetChallengeEx; pCardData->pfnCardAuthenticateEx = CardAuthenticateEx; pCardData->pfnCardChangeAuthenticatorEx = CardChangeAuthenticatorEx; pCardData->pfnCardDeauthenticateEx = CardDeauthenticateEx; pCardData->pfnCardGetContainerProperty = CardGetContainerProperty; pCardData->pfnCardSetContainerProperty = CardSetContainerProperty; pCardData->pfnCardGetProperty = CardGetProperty; pCardData->pfnCardSetProperty = CardSetProperty; if (pCardData->dwVersion >= CARD_DATA_VERSION_SEVEN) { pCardData->pfnMDImportSessionKey = MDImportSessionKey; pCardData->pfnMDEncryptData = MDEncryptData; pCardData->pfnCardImportSessionKey = CardImportSessionKey; pCardData->pfnCardGetSharedKeyHandle = CardGetSharedKeyHandle; pCardData->pfnCardGetAlgorithmProperty = CardGetAlgorithmProperty; pCardData->pfnCardGetKeyProperty = CardGetKeyProperty; pCardData->pfnCardSetKeyProperty = CardSetKeyProperty; pCardData->pfnCardProcessEncryptedData = CardProcessEncryptedData; pCardData->pfnCardDestroyKey = CardDestroyKey; pCardData->pfnCardCreateContainerEx = CardCreateContainerEx; } } } 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 DWORD associate_card(PCARD_DATA pCardData) { VENDOR_SPECIFIC *vs; int r; struct sc_app_info *app_generic; struct sc_aid *aid; MD_FUNC_CALLED(pCardData, 1); 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); /* * set the addresses of the reader and card handles * Our pcsc code will use these when we call sc_ctx_use_reader * We use the address of the handles as provided in the pCardData */ vs->hSCardCtx = pCardData->hSCardCtx; vs->hScard = pCardData->hScard; /* set the provided reader and card handles into ctx */ 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) MD_FUNC_RETURN(pCardData, 1, SCARD_E_COMM_DATA_LOST); 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)); 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)); 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); } vs->initialized = TRUE; MD_FUNC_RETURN(pCardData, 1, SCARD_S_SUCCESS); } 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"); vs = (VENDOR_SPECIFIC*)(pCardData->pvVendorSpecific); if (!vs) { logprintf(pCardData, 1, "disassociate_card called without vendor specific data\n"); return; } 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"); sc_pkcs15_unbind(vs->p15card); vs->p15card = NULL; } if(vs->card) { logprintf(pCardData, 6, "sc_disconnect_card\n"); sc_disconnect_card(vs->card); vs->card = NULL; } vs->reader = NULL; vs->hSCardCtx = -1; vs->hScard = -1; vs->initialized = FALSE; } BOOL APIENTRY DllMain( HINSTANCE hinstDLL, DWORD ul_reason_for_call, LPVOID lpReserved ) { CHAR name[MAX_PATH + 1] = "\0"; char *reason = ""; GetModuleFileNameA(GetModuleHandle(NULL),name,MAX_PATH); switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: reason = "Attach Process"; break; case DLL_THREAD_ATTACH: reason = "Attach Thread"; break; case DLL_THREAD_DETACH: reason = "Detach Thread"; break; case DLL_PROCESS_DETACH: reason = "Detach Process"; break; } logprintf(NULL, 8, "\n********** DllMain Module(handle:0x%p) '%s'; reason='%s'; Reserved=%p; P:%lu; T:%lu\n", hinstDLL, name, reason, lpReserved, (unsigned long)GetCurrentProcessId(), (unsigned long)GetCurrentThreadId()); switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: g_inst = hinstDLL; #ifdef ENABLE_NOTIFY sc_notify_instance = hinstDLL; sc_notify_init(); #endif break; case DLL_PROCESS_DETACH: #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; } #ifdef _MANAGED #pragma managed(pop) #endif #endif OpenSC-0.26.1/src/minidriver/minidriver.exports000066400000000000000000000000231474147347300214750ustar00rootroot00000000000000CardAcquireContext OpenSC-0.26.1/src/minidriver/opensc-minidriver.dll.manifest000066400000000000000000000010041474147347300236360ustar00rootroot00000000000000 OpenSC-0.26.1/src/minidriver/opensc-minidriver.inf.in000066400000000000000000000073151474147347300224520ustar00rootroot00000000000000 [Version] Signature="$Windows NT$" Class=SmartCard ClassGuid={990A2BD7-E738-46c7-B26F-1CF8FB9F1391} Provider=%ProviderName% CatalogFile=delta.cat DriverVer=05/02/2010,@OPENSC_VERSION_MAJOR@,@OPENSC_VERSION_MINOR@,@OPENSC_VERSION_FIX@,0 [Manufacturer] %ProviderName%=Minidriver,NTamd64,NTamd64.6.1,NTx86,NTx86.6.1 [Minidriver.NTamd64] %CardDeviceName%=Minidriver64_Install,SCFILTER\CID_00640181010c829000 [Minidriver.NTx86] %CardDeviceName%=Minidriver32_Install,SCFILTER\CID_00640181010c829000 [Minidriver.NTamd64.6.1] %CardDeviceName%=Minidriver64_61_Install,SCFILTER\CID_00640181010c829000 [Minidriver.NTx86.6.1] %CardDeviceName%=Minidriver32_61_Install,SCFILTER\CID_00640181010c829000 [DefaultInstall] CopyFiles=x86_CopyFiles AddReg=AddRegDefault [DefaultInstall.ntamd64] CopyFiles=amd64_CopyFiles CopyFiles=wow64_CopyFiles AddReg=AddRegWOW64 AddReg=AddRegDefault [DefaultInstall.NTx86] CopyFiles=x86_CopyFiles AddReg=AddRegDefault [DefaultInstall.ntamd64.6.1] AddReg=AddRegWOW64 AddReg=AddRegDefault [DefaultInstall.NTx86.6.1] AddReg=AddRegDefault [SourceDisksFiles] %SmartCardCardModule%=1 %SmartCardCardModule64%=1 [SourceDisksNames] 1 = %MediaDescription% [Minidriver64_Install.NT] CopyFiles=amd64_CopyFiles CopyFiles=wow64_CopyFiles AddReg=AddRegWOW64 AddReg=AddRegDefault [Minidriver64_61_Install.NT] AddReg=AddRegWOW64 AddReg=AddRegDefault Include=umpass.inf Needs=UmPass [Minidriver32_Install.NT] CopyFiles=x86_CopyFiles AddReg=AddRegDefault [Minidriver32_61_Install.NT] AddReg=AddRegDefault Include=umpass.inf Needs=UmPass [Minidriver64_61_Install.NT.Services] Include=umpass.inf Needs=UmPass.Services [Minidriver32_61_Install.NT.Services] Include=umpass.inf Needs=UmPass.Services [Minidriver64_61_Install.NT.HW] Include=umpass.inf Needs=UmPass.HW [Minidriver64_61_Install.NT.CoInstallers] Include=umpass.inf Needs=UmPass.CoInstallers [Minidriver64_61_Install.NT.Interfaces] Include=umpass.inf Needs=UmPass.Interfaces [Minidriver32_61_Install.NT.HW] Include=umpass.inf Needs=UmPass.HW [Minidriver32_61_Install.NT.CoInstallers] Include=umpass.inf Needs=UmPass.CoInstallers [Minidriver32_61_Install.NT.Interfaces] Include=umpass.inf Needs=UmPass.Interfaces [amd64_CopyFiles] ;%SmartCardCardModule%,%SmartCardCardModule64% [x86_CopyFiles] ;%SmartCardCardModule% [wow64_CopyFiles] ;%SmartCardCardModule64% [AddRegWOW64] HKLM, %SmartCardNameWOW64%,"ATR",0x00000001,3f,69,00,00,00,64,01,00,00,00,80,90,00 HKLM, %SmartCardNameWOW64%,"ATRMask",0x00000001,ff,ff,ff,ff,ff,ff,ff,00,00,00,f0,ff,ff HKLM, %SmartCardNameWOW64%,"Crypto Provider",0x00000000,"Microsoft Base Smart Card Crypto Provider" HKLM, %SmartCardNameWOW64%,"Smart Card Key Storage Provider",0x00000000,"Microsoft Smart Card Key Storage Provider" HKLM, %SmartCardNameWOW64%,"80000001",0x00000000,%SmartCardCardModule64% [AddRegDefault] HKLM, %SmartCardName%,"ATR",0x00000001,3f,69,00,00,00,64,01,00,00,00,80,90,00 HKLM, %SmartCardName%,"ATRMask",0x00000001,ff,ff,ff,ff,ff,ff,ff,00,00,00,f0,ff,ff HKLM, %SmartCardName%,"Crypto Provider",0x00000000,"Microsoft Base Smart Card Crypto Provider" HKLM, %SmartCardName%,"Smart Card Key Storage Provider",0x00000000,"Microsoft Smart Card Key Storage Provider" HKLM, %SmartCardName%,"80000001",0x00000000,%SmartCardCardModule% [DestinationDirs] amd64_CopyFiles=10,system32 x86_CopyFiles=10,system32 wow64_CopyFiles=10,syswow64 ; =================== Generic ================================== [Strings] ProviderName ="OpenSC" MediaDescription="OpenSC Smart Card Minidriver Installation Disk" CardDeviceName="OpenSC Minidriver" SmartCardName="SOFTWARE\Microsoft\Cryptography\Calais\SmartCards\Cev Westcos" SmartCardNameWOW64="SOFTWARE\Wow6432Node\Microsoft\Cryptography\Calais\SmartCards\Cev Westcos" SmartCardCardModule="opensc-minidriver.dll" OpenSC-0.26.1/src/minidriver/versioninfo-minidriver.rc.in000066400000000000000000000027271474147347300233560ustar00rootroot00000000000000#include #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", "@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 OpenSC-0.26.1/src/pkcs11/000077500000000000000000000000001474147347300146365ustar00rootroot00000000000000OpenSC-0.26.1/src/pkcs11/Makefile.am000066400000000000000000000074361474147347300167040ustar00rootroot00000000000000include $(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 opensc-pkcs11.dll.manifest 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_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 = \ $(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 \ $(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) opensc_pkcs11_la_SOURCES = $(OPENSC_PKCS11_SRC) $(OPENSC_PKCS11_INC) opensc_pkcs11_la_CFLAGS = $(OPENSC_PKCS11_CFLAGS) opensc_pkcs11_la_LIBADD = $(OPENSC_PKCS11_LIBS) 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) $(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) $(PTHREAD_LIBS) pkcs11_spy_la_LDFLAGS = $(AM_LDFLAGS) \ -export-symbols "$(srcdir)/pkcs11.exports" \ -module -shared -avoid-version -no-undefined 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 install-exec-hook: $(MKDIR_P) "$(DESTDIR)$(libdir)" 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 OpenSC-0.26.1/src/pkcs11/Makefile.mak000066400000000000000000000030061474147347300170440ustar00rootroot00000000000000TOPDIR = ..\.. TARGET1 = opensc-pkcs11.dll TARGET3 = pkcs11-spy.dll OBJECTS = pkcs11-global.obj pkcs11-session.obj pkcs11-object.obj misc.obj slot.obj \ mechanism.obj openssl.obj framework-pkcs15.obj framework-pkcs15init.obj \ 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 \ $(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) $(TARGET3) !INCLUDE $(TOPDIR)\win32\Make.rules.mak $(TARGET1): $(OBJECTS) $(LIBS) link $(LINKFLAGS) /dll /implib:$*.lib /out:$(TARGET1) $(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 if EXIST $(TARGET1).manifest mt -manifest $(TARGET1).manifest -outputresource:$(TARGET1);2 $(TARGET3): $(OBJECTS3) $(LIBS3) link $(LINKFLAGS) /dll /implib:$*.lib /out:$(TARGET3) $(OBJECTS3) $(LIBS3) $(OPENSSL_LIB) gdi32.lib advapi32.lib shlwapi.lib if EXIST $(TARGET3).manifest mt -manifest $(TARGET3).manifest -outputresource:$(TARGET3);2 OpenSC-0.26.1/src/pkcs11/debug.c000066400000000000000000000146411474147347300160760ustar00rootroot00000000000000/* * Debugging stuff for pkcs11 * * 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 */ #include "config.h" #include #include #include "sc-pkcs11.h" #define DUMP_TEMPLATE_MAX 32 struct fmap { CK_ULONG value; const char * name; const char * (*print)(int level, struct fmap *, void *, size_t); struct fmap * map; }; #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 } #define b(x) { (x), #x, sc_pkcs11_print_bool, NULL } #define s(x) { (x), #x, sc_pkcs11_print_string, NULL } static void sc_pkcs11_print_attr(int level, const char *, unsigned int, const char *, const char *, CK_ATTRIBUTE_PTR); static const char * sc_pkcs11_print_value(int level, struct fmap *, void *, size_t); static struct fmap * sc_pkcs11_map_ulong(int level, struct fmap *, CK_ULONG); static const char * sc_pkcs11_print_ulong(int level, struct fmap *, void *, size_t); static const char * sc_pkcs11_print_bool(int level, struct fmap *, void *, size_t); 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_CERTIFICATE_TYPE[] = { _(CKC_X_509), _(CKC_X_509_ATTR_CERT), { 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_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 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), { 0, NULL, NULL, NULL } }; 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) { if (ulCount == 0) { sc_do_log(context, level, file, line, function, "%s: empty template\n", info); return; } while (ulCount--) sc_pkcs11_print_attr(level, file, line, function, info, pTemplate++); } static void sc_pkcs11_print_attr(int level, const char *file, unsigned int line, const char *function, const char *info, CK_ATTRIBUTE_PTR attr) { struct fmap *fm; const char * value; fm = sc_pkcs11_map_ulong(level, p11_attr_names, attr->type); if (attr->pValue == NULL) { value = ""; } else { value = sc_pkcs11_print_value(level, fm, attr->pValue, attr->ulValueLen); } if (fm == NULL) { sc_do_log(context, level, file, line, function, "%s: Attribute 0x%lx = %s\n", info, attr->type, value); } else { sc_do_log(context, level, file, line, function, "%s: %s = %s\n", info, fm->name, value); } } static const char *sc_pkcs11_print_value(int level, struct fmap *fm, void *ptr, size_t count) { static char buffer[4 * DUMP_TEMPLATE_MAX + 1] = ""; if (count == (CK_ULONG)-1) return ""; if (!fm || !fm->print) { unsigned char *value = (unsigned char*) ptr; char *p; if (count > DUMP_TEMPLATE_MAX) count = DUMP_TEMPLATE_MAX; for (p = buffer; count--; value++) p += sprintf(p, "%02X", *value); return buffer; } return fm->print(level, fm, ptr, count); } static const char *sc_pkcs11_print_ulong(int level, struct fmap *fm, void *ptr, size_t count) { static char buffer[64]; CK_ULONG value; if (count == sizeof(CK_ULONG)) { memcpy(&value, ptr, count); if ((fm = sc_pkcs11_map_ulong(level, fm->map, value)) != NULL) return fm->name; sprintf(buffer, "0x%lx", (unsigned long) value); return buffer; } return sc_pkcs11_print_value(level, NULL, ptr, count); } static const char *sc_pkcs11_print_bool(int level, struct fmap *fm, void *ptr, size_t count) { CK_BBOOL value; if (count == sizeof(CK_BBOOL)) { memcpy(&value, ptr, count); if (value) return "TRUE"; return "FALSE"; } return sc_pkcs11_print_value(level, NULL, ptr, count); } static const char *sc_pkcs11_print_string(int level, struct fmap *fm, void *ptr, size_t count) { static char buffer[128]; if (count >= sizeof(buffer)) count = sizeof(buffer)-1; memcpy(buffer, ptr, count); buffer[count] = 0; return buffer; } static struct fmap *sc_pkcs11_map_ulong(int level, struct fmap *fm, CK_ULONG value) { for (; fm && fm->name; fm++) { if (fm->value == value) return fm; } return NULL; } OpenSC-0.26.1/src/pkcs11/framework-pkcs15.c000066400000000000000000006360441474147347300201200ustar00rootroot00000000000000/* * framework-pkcs15.c: PKCS#15 framework and related objects * * Copyright (C) 2002 Timo Teräs * * 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 "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 #if OPENSSL_VERSION_NUMBER >= 0x30000000L #include #endif #else #define SHA_DIGEST_LENGTH 20 #endif #include "sc-pkcs11.h" #ifdef USE_PKCS15_INIT #include "pkcs15init/pkcs15-init.h" #endif struct pkcs15_slot_data { struct sc_pkcs15_object *auth_obj; }; #define slot_data(p) ((struct pkcs15_slot_data *) (p)) #define slot_data_auth(p) (((p) && slot_data(p)) ? slot_data(p)->auth_obj : NULL) #define slot_data_auth_info(p) (((p) && slot_data_auth(p))? \ (struct sc_pkcs15_auth_info *) slot_data_auth(p)->data : NULL) #define check_attribute_buffer(attr,size) \ if (attr->pValue == NULL_PTR) { \ attr->ulValueLen = size; \ return CKR_OK; \ } \ if (attr->ulValueLen < size) { \ attr->ulValueLen = size; \ return CKR_BUFFER_TOO_SMALL; \ } \ attr->ulValueLen = size; #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; unsigned int user_puk_len; }; struct pkcs15_any_object { struct sc_pkcs11_object base; unsigned int refcount; size_t size; struct sc_pkcs15_object * p15_object; struct pkcs15_pubkey_object * related_pubkey; struct pkcs15_cert_object * related_cert; struct pkcs15_prkey_object * related_privkey; }; struct pkcs15_cert_object { struct pkcs15_any_object base; struct sc_pkcs15_cert_info * cert_info; struct sc_pkcs15_cert * cert_data; }; #define cert_flags base.base.flags #define cert_p15obj base.p15_object #define cert_pubkey base.related_pubkey #define cert_issuer base.related_cert #define cert_prvkey base.related_privkey struct pkcs15_prkey_object { struct pkcs15_any_object base; struct sc_pkcs15_prkey_info * prv_info; struct sc_pkcs15_pubkey * pub_data; }; #define prv_flags base.base.flags #define prv_p15obj base.p15_object #define prv_pubkey base.related_pubkey #define prv_next base.related_privkey struct pkcs15_pubkey_object { struct pkcs15_any_object base; struct sc_pkcs15_pubkey_info * pub_info; /* NULL for key extracted from cert */ struct sc_pkcs15_pubkey * pub_data; }; #define pub_flags base.base.flags #define pub_p15obj base.p15_object #define pub_genfrom base.related_cert #define __p15_type(obj) (((obj) && (obj)->p15_object)? ((obj)->p15_object->type) : (unsigned int)-1) #define is_privkey(obj) ((__p15_type(obj) & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_PRKEY) #define is_pubkey(obj) ((__p15_type(obj) & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_PUBKEY) #define is_cert(obj) (__p15_type(obj) == SC_PKCS15_TYPE_CERT_X509) struct pkcs15_data_object { struct pkcs15_any_object base; struct sc_pkcs15_data_info *info; struct sc_pkcs15_data *value; }; #define data_flags base.base.flags #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; struct sc_pkcs15_skey_info *info; 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 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 }; const unsigned int gostr3410_paramset_A_oid[] = {1, 2, 643, 2, 2, 35, 1, (unsigned int)-1}; const CK_BYTE gostr3410_paramset_B_encoded_oid[] = { 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x02 }; const unsigned int gostr3410_paramset_B_oid[] = {1, 2, 643, 2, 2, 35, 2, (unsigned int)-1}; 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}; static const struct { const CK_BYTE *encoded_oid; const unsigned int encoded_oid_size; const unsigned int *oid; const unsigned int oid_size; unsigned char oid_id; } gostr3410_param_oid [] = { { &gostr3410_paramset_A_encoded_oid[0], sizeof(gostr3410_paramset_A_encoded_oid), &gostr3410_paramset_A_oid[0], sizeof(gostr3410_paramset_A_oid), SC_PKCS15_PARAMSET_GOSTR3410_A }, { &gostr3410_paramset_B_encoded_oid[0], sizeof(gostr3410_paramset_B_encoded_oid), &gostr3410_paramset_B_oid[0], sizeof(gostr3410_paramset_B_oid), SC_PKCS15_PARAMSET_GOSTR3410_B }, { &gostr3410_paramset_C_encoded_oid[0], sizeof(gostr3410_paramset_C_encoded_oid), &gostr3410_paramset_C_oid[0], sizeof(gostr3410_paramset_C_oid), SC_PKCS15_PARAMSET_GOSTR3410_C } }; 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; const unsigned int *oid; const unsigned int oid_size; } gostr3410_hash_param_oid [] = { { &gostr3411_94_cryptopro_paramset_encoded_oid[0], sizeof(gostr3411_94_cryptopro_paramset_encoded_oid), &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); static CK_RV get_public_exponent(struct sc_pkcs15_pubkey *, CK_ATTRIBUTE_PTR); static CK_RV get_modulus(struct sc_pkcs15_pubkey *, CK_ATTRIBUTE_PTR); static CK_RV get_modulus_bits(struct sc_pkcs15_pubkey *, CK_ATTRIBUTE_PTR); static CK_RV get_usage_bit(unsigned int usage, CK_ATTRIBUTE_PTR attr); static CK_RV get_gostr3410_params(const u8 *, size_t, CK_ATTRIBUTE_PTR); 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 *); #ifdef USE_PKCS15_INIT static CK_RV set_gost3410_params(struct sc_pkcs15init_prkeyargs *, struct sc_pkcs15init_pubkeyargs *, CK_ATTRIBUTE_PTR, CK_ULONG, CK_ATTRIBUTE_PTR, CK_ULONG); static CK_RV pkcs15_create_slot(struct sc_pkcs11_card *p11card, struct pkcs15_fw_data *fw_data, struct sc_pkcs15_object *auth, struct sc_app_info *app, struct sc_pkcs11_slot **out); static int pkcs11_get_pin_callback(struct sc_profile *profile, int id, const struct sc_pkcs15_auth_info *info, const char *label, unsigned char *pinbuf, size_t *pinsize); static struct sc_pkcs15init_callbacks pkcs15init_callbacks = { pkcs11_get_pin_callback, /* get_pin() */ NULL }; static char *pkcs15init_sopin = NULL; static size_t pkcs15init_sopin_len = 0; static int pkcs11_get_pin_callback(struct sc_profile *profile, int id, const struct sc_pkcs15_auth_info *info, const char *label, unsigned char *pinbuf, size_t *pinsize) { char *secret = NULL; size_t len = 0; if (info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return SC_ERROR_NOT_SUPPORTED; sc_log(context, "pkcs11_get_pin_callback() auth-method %X", info->auth_method); if (info->auth_method == SC_AC_CHV) { unsigned int flags = info->attrs.pin.flags; sc_log(context, "pkcs11_get_pin_callback() PIN flags %X", flags); if ((flags & SC_PKCS15_PIN_FLAG_SO_PIN) && !(flags & SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN)) { if (pkcs15init_sopin_len) secret = pkcs15init_sopin; } } if (secret) len = strlen(secret); sc_log(context, "pkcs11_get_pin_callback() secret '%s'", secret ? secret : ""); if (!secret) return SC_ERROR_OBJECT_NOT_FOUND; if (len > *pinsize) return SC_ERROR_BUFFER_TOO_SMALL; memcpy(pinbuf, secret, len + 1); *pinsize = len; return 0; } #endif /* 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; 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; if (!fw_data || !fw_data->p15_card) continue; file_app = fw_data->p15_card->file_app; if (app_info && file_app) { if (file_app->path.len != app_info->path.len) continue; if (file_app->path.aid.len != app_info->path.aid.len) continue; if (memcmp(file_app->path.aid.value, app_info->path.aid.value, app_info->path.aid.len)) continue; if (memcmp(file_app->path.value, app_info->path.value, app_info->path.len)) continue; } out = fw_data; if (out_idx) *out_idx = idx; break; } return out; } /* PKCS#15 Framework */ static CK_RV pkcs15_bind(struct sc_pkcs11_card *p11card, struct sc_app_info *app_info) { struct pkcs15_fw_data *fw_data = NULL; struct sc_aid *aid = app_info ? &app_info->aid : NULL; int rc, idx; 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; if (idx == SC_PKCS11_FRAMEWORK_DATA_MAX_NUM) return CKR_USER_TOO_MANY_TYPES; if (!(fw_data = calloc(1, sizeof(*fw_data)))) return CKR_HOST_MEMORY; p11card->fws_data[idx] = fw_data; rc = sc_pkcs15_bind(p11card->card, aid, &fw_data->p15_card); if (rc != SC_SUCCESS) { sc_log(context, "sc_pkcs15_bind failed: %d", rc); return sc_to_cryptoki_error(rc, NULL); } /* Mechanisms are registered globally per card. Checking * p11card->nmechanisms avoids registering the same mechanisms twice for a * card with multiple slots. */ if (!p11card->nmechanisms) { ck_rv = register_mechanisms(p11card); if (ck_rv != CKR_OK) { sc_log(context, "cannot register mechanisms; CKR 0x%lX", ck_rv); return ck_rv; } } return CKR_OK; } static CK_RV pkcs15_unbind(struct sc_pkcs11_card *p11card) { unsigned int i, idx; int rv = SC_SUCCESS; if (!p11card) return CKR_TOKEN_NOT_RECOGNIZED; for (idx=0; p11card && idxfws_data[idx]; if (!fw_data) break; for (i = 0; i < fw_data->num_objects; i++) { struct pkcs15_any_object *obj = fw_data->objects[i]; /* use object specific release method if existing */ if (obj->base.ops && obj->base.ops->release) obj->base.ops->release(obj); else __pkcs15_release_object(obj); } unlock_card(fw_data); if (fw_data->p15_card) { rv = sc_pkcs15_unbind(fw_data->p15_card); } fw_data->p15_card = NULL; free(fw_data); p11card->fws_data[idx] = NULL; } return sc_to_cryptoki_error(rv, NULL); } static void pkcs15_init_token_info(struct sc_pkcs15_card *p15card, CK_TOKEN_INFO_PTR pToken) { scconf_block *conf_block = NULL; char *model = NULL; conf_block = sc_get_conf_block(p15card->card->ctx, "framework", "pkcs15", 1); if (conf_block && p15card->file_app) { scconf_block **blocks = NULL; char str_path[SC_MAX_AID_STRING_SIZE]; memset(str_path, 0, sizeof(str_path)); sc_bin_to_hex(p15card->file_app->path.value, p15card->file_app->path.len, str_path, sizeof(str_path), 0); blocks = scconf_find_blocks(p15card->card->ctx->conf, conf_block, "application", str_path); if (blocks) { if (blocks[0]) model = (char *)scconf_get_str(blocks[0], "model", NULL); free(blocks); } } 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)); if (p15card->tokeninfo) { strcpy_bp(pToken->manufacturerID, p15card->tokeninfo->manufacturer_id, 32); /* 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; pToken->ulSessionCount = 0; /* FIXME */ pToken->ulMaxRwSessionCount = CK_EFFECTIVELY_INFINITE; pToken->ulRwSessionCount = 0; /* FIXME */ pToken->ulTotalPublicMemory = CK_UNAVAILABLE_INFORMATION; pToken->ulFreePublicMemory = CK_UNAVAILABLE_INFORMATION; pToken->ulTotalPrivateMemory = CK_UNAVAILABLE_INFORMATION; pToken->ulFreePrivateMemory = CK_UNAVAILABLE_INFORMATION; pToken->hardwareVersion.major = p15card->card->version.hw_major; pToken->hardwareVersion.minor = p15card->card->version.hw_minor; pToken->firmwareVersion.major = p15card->card->version.fw_major; pToken->firmwareVersion.minor = p15card->card->version.fw_minor; } #ifdef USE_PKCS15_INIT static char * set_cka_label(CK_ATTRIBUTE_PTR attr, char *label) { char *l = (char *)attr->pValue; unsigned long len = attr->ulValueLen; if (len >= SC_PKCS15_MAX_LABEL_SIZE) len = SC_PKCS15_MAX_LABEL_SIZE-1; memcpy(label, l, len); label[len] = '\0'; return label; } #endif static int __pkcs15_create_object(struct pkcs15_fw_data *fw_data, struct pkcs15_any_object **result, struct sc_pkcs15_object *p15_object, struct sc_pkcs11_object_ops *ops, size_t size) { struct pkcs15_any_object *obj; if (fw_data->num_objects >= MAX_OBJECTS) return SC_ERROR_TOO_MANY_OBJECTS; if (!(obj = calloc(1, size))) return SC_ERROR_OUT_OF_MEMORY; fw_data->objects[fw_data->num_objects++] = obj; obj->base.ops = ops; obj->p15_object = p15_object; obj->refcount = 1; obj->size = size; *result = obj; return SC_SUCCESS; } static int __pkcs15_release_object(struct pkcs15_any_object *obj) { if (--(obj->refcount) != 0) return obj->refcount; sc_mem_clear(obj, obj->size); free(obj); return 0; } #ifdef USE_PKCS15_INIT static int __pkcs15_delete_object(struct pkcs15_fw_data *fw_data, struct pkcs15_any_object *obj) { unsigned int i; if (fw_data->num_objects == 0) return SC_ERROR_INTERNAL; for (i = 0; i < fw_data->num_objects; ++i) { if (fw_data->objects[i] == obj) { fw_data->objects[i] = fw_data->objects[--fw_data->num_objects]; if (__pkcs15_release_object(obj) > 0) return SC_ERROR_INTERNAL; return SC_SUCCESS; } } return SC_ERROR_OBJECT_NOT_FOUND; } #endif 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_object *auth; const char *name; CK_RV rv; 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) { sc_log(context, "C_GetTokenInfo() get token: rv 0x%lX", rv); goto out; } if (slot->p11card == NULL) { if (slot->slot_info.flags & CKF_TOKEN_PRESENT) { rv = CKR_TOKEN_NOT_RECOGNIZED; } else { rv = CKR_TOKEN_NOT_PRESENT; } goto out; } fw_data = (struct pkcs15_fw_data *) slot->p11card->fws_data[slot->fw_data_idx]; if (!fw_data) { rv = sc_to_cryptoki_error(SC_ERROR_INTERNAL, "C_GetTokenInfo"); goto out; } /* 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); 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); 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) { if (pin_info->tries_left == 1 || pin_info->max_tries == 1) slot->token_info.flags |= CKF_USER_PIN_FINAL_TRY; else if (pin_info->tries_left == 0) slot->token_info.flags |= CKF_USER_PIN_LOCKED; else if (pin_info->max_tries > 1 && pin_info->tries_left < pin_info->max_tries) slot->token_info.flags |= CKF_USER_PIN_COUNT_LOW; } } 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(); return rv; } static int public_key_created(struct pkcs15_fw_data *fw_data, const struct sc_pkcs15_id *id, struct pkcs15_any_object **obj2) { size_t ii; for(ii=0; iinum_objects; ii++) { struct pkcs15_any_object *any_object = fw_data->objects[ii]; struct sc_pkcs15_object *p15_object = any_object->p15_object; if (!p15_object) continue; if ((p15_object->type & SC_PKCS15_TYPE_CLASS_MASK) != SC_PKCS15_TYPE_PUBKEY) continue; if (sc_pkcs15_compare_id(id, &((struct sc_pkcs15_pubkey_info *)p15_object->data)->id)) { if (obj2) *obj2 = any_object; return SC_SUCCESS; } } return SC_ERROR_OBJECT_NOT_FOUND; } static void pkcs15_cert_extract_label(struct pkcs15_cert_object *cert) { if (!cert || !cert->cert_p15obj || !cert->cert_data) return; sc_log(context, "pkcs15_cert_extract_label() called. Current label: %s", cert->cert_p15obj->label); /* if we didn't get a label, set one based on the CN */ if (*cert->cert_p15obj->label == '\0') { /* can't be NULL -- static array */ static const struct sc_object_id cn_oid = {{ 2, 5, 4, 3, -1 }}; u8 *cn_name = NULL; size_t cn_len = 0; int rv = sc_pkcs15_get_name_from_dn(context, 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", (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'; } free(cn_name); } } static int __pkcs15_create_cert_object(struct pkcs15_fw_data *fw_data, struct sc_pkcs15_object *cert, struct pkcs15_any_object **cert_object) { 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; if (cert->flags & SC_PKCS15_CO_FLAG_PRIVATE) { /* is the cert private? */ p15_cert = NULL; /* will read cert when needed */ } else { 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, &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); return rv; } object->cert_info = p15_info; object->cert_data = p15_cert; /* Corresponding public key */ rv = public_key_created(fw_data, &p15_info->id, &any_object2); if (rv != SC_SUCCESS) 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; if (p15_cert) { /* make a copy of public key from the cert */ if (!obj2->pub_data) rv = sc_pkcs15_pubkey_from_cert(context, &p15_cert->data, &obj2->pub_data); if (rv < 0) return rv; } obj2->pub_genfrom = object; object->cert_pubkey = obj2; /* Find missing labels for certificate */ pkcs15_cert_extract_label(object); if (cert_object != NULL) *cert_object = any_object; return 0; } 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; /* Read public key from card */ /* Attempt to read pubkey from card or file. * During initialization process, the key may have been created * and saved as a file before the certificate has been created. */ if (pubkey->flags & SC_PKCS15_CO_FLAG_PRIVATE) { /* is the key private? */ sc_log(context, "No pubkey"); p15_key = NULL; /* will read key when needed */ } else { /* 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"); sc_pkcs15_dup_pubkey(context, (struct sc_pkcs15_pubkey *) pubkey->emulated, &p15_key); } else { sc_log(context, "Get pubkey from PKCS#15 object"); rv = sc_pkcs15_read_pubkey(fw_data->p15_card, pubkey, &p15_key); if (rv < 0) p15_key = NULL; } } /* Public key 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 { sc_pkcs15_free_pubkey(p15_key); } 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 = any_object; return rv; } 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_any_object *any_object = NULL; struct pkcs15_prkey_object *object = NULL; int rv; 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 = any_object; return rv; } 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, &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; } if (data_object != NULL) *data_object = any_object; 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; } 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, &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; if (skey_object != NULL) *skey_object = any_object; return rv; } static int pkcs15_create_pkcs11_objects(struct pkcs15_fw_data *fw_data, int p15_type, const char *name, int (*create)(struct pkcs15_fw_data *, struct sc_pkcs15_object *, struct pkcs15_any_object **any_object)) { struct sc_pkcs15_object *p15_object[MAX_OBJECTS]; int i, count, rv; rv = count = sc_pkcs15_get_objects(fw_data->p15_card, p15_type, p15_object, MAX_OBJECTS); if (rv >= 0) sc_log(context, "Found %d %s%s", count, name, (count == 1)? "" : "s"); for (i = 0; rv >= 0 && i < count; i++) rv = create(fw_data, p15_object[i], NULL); return count; } static void __pkcs15_prkey_bind_related(struct pkcs15_fw_data *fw_data, struct pkcs15_prkey_object *pk) { struct sc_pkcs15_id *id = &pk->prv_info->id; unsigned int i; sc_log(context, "Object is a private key and has id %s", sc_pkcs15_print_id(id)); for (i = 0; i < fw_data->num_objects; i++) { struct pkcs15_any_object *obj = fw_data->objects[i]; if (obj->base.flags & SC_PKCS11_OBJECT_HIDDEN) continue; if (is_privkey(obj) && obj != (struct pkcs15_any_object *) pk) { /* merge private keys with the same ID and * different usage bits */ struct pkcs15_prkey_object *other, **pp; other = (struct pkcs15_prkey_object *) obj; if (sc_pkcs15_compare_id(&other->prv_info->id, id)) { obj->base.flags |= SC_PKCS11_OBJECT_HIDDEN; for (pp = &pk->prv_next; *pp; pp = &(*pp)->prv_next) ; *pp = (struct pkcs15_prkey_object *) obj; } } else if (is_pubkey(obj) && !pk->prv_pubkey) { struct pkcs15_pubkey_object *pubkey; pubkey = (struct pkcs15_pubkey_object *) obj; if (sc_pkcs15_compare_id(&pubkey->pub_info->id, id)) { sc_log(context, "Associating object %d as public key", i); pk->prv_pubkey = pubkey; if (pubkey->pub_data) { sc_pkcs15_dup_pubkey(context, pubkey->pub_data, &pk->pub_data); if (pk->prv_info->modulus_length == 0) pk->prv_info->modulus_length = pubkey->pub_info->modulus_length; } } } } } static void __pkcs15_cert_bind_related(struct pkcs15_fw_data *fw_data, struct pkcs15_cert_object *cert) { struct sc_pkcs15_cert *c1 = cert->cert_data; struct sc_pkcs15_id *id = &cert->cert_info->id; unsigned int i; sc_log(context, "Object is a certificate and has id %s", sc_pkcs15_print_id(id)); /* Loop over all objects to see if we find the certificate of * the issuer and the associated private key */ for (i = 0; i < fw_data->num_objects; i++) { struct pkcs15_any_object *obj = fw_data->objects[i]; if (is_cert(obj) && obj != (struct pkcs15_any_object *) cert) { struct pkcs15_cert_object *cert2; struct sc_pkcs15_cert *c2; cert2 = (struct pkcs15_cert_object *) obj; c2 = cert2->cert_data; if (!c1 || !c2 || !c1->issuer_len || !c2->subject_len) continue; 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)); cert->cert_issuer = (struct pkcs15_cert_object *) obj; return; } } else if (is_privkey(obj) && !cert->cert_prvkey) { struct pkcs15_prkey_object *pk; pk = (struct pkcs15_prkey_object *) obj; if (sc_pkcs15_compare_id(&pk->prv_info->id, id)) { sc_log(context, "Associating object %d as private key", i); cert->cert_prvkey = pk; } } } } static void pkcs15_bind_related_objects(struct pkcs15_fw_data *fw_data) { unsigned int i; /* Loop over all private keys and attached related certificate * and/or public key */ for (i = 0; i < fw_data->num_objects; i++) { struct pkcs15_any_object *obj = fw_data->objects[i]; if (obj->base.flags & SC_PKCS11_OBJECT_HIDDEN) continue; sc_log(context, "Looking for objects related to object %d", i); if (is_privkey(obj)) __pkcs15_prkey_bind_related(fw_data, (struct pkcs15_prkey_object *) obj); else if (is_cert(obj)) __pkcs15_cert_bind_related(fw_data, (struct pkcs15_cert_object *) obj); } } /* We deferred reading of the cert until needed, as it may be * a private object, so we must wait till login to read */ static int check_cert_data_read(struct pkcs15_fw_data *fw_data, struct pkcs15_cert_object *cert) { struct pkcs15_pubkey_object *obj2; int rv; int private_obj; if (!cert) return SC_ERROR_OBJECT_NOT_FOUND; if (cert->cert_data) return 0; 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; obj2 = cert->cert_pubkey; /* make a copy of public key from the cert data */ if (!obj2->pub_data) rv = sc_pkcs15_pubkey_from_cert(context, &cert->cert_data->data, &obj2->pub_data); /* Find missing labels for certificate */ pkcs15_cert_extract_label(cert); /* now that we have the cert and pub key, lets see if we can bind anything else */ pkcs15_bind_related_objects(fw_data); return rv; } static void pkcs15_add_object(struct sc_pkcs11_slot *slot, struct pkcs15_any_object *obj, CK_OBJECT_HANDLE_PTR pHandle) { unsigned int i; struct pkcs15_fw_data *card_fw_data; CK_OBJECT_HANDLE handle = (CK_OBJECT_HANDLE)(uintptr_t)obj; /* cast pointer to long, will truncate on Win64 */ if (obj == NULL || slot == NULL) return; if (obj->base.flags & (SC_PKCS11_OBJECT_HIDDEN | SC_PKCS11_OBJECT_RECURS)) return; if (list_contains(&slot->objects, obj)) return; if (pHandle != NULL) *pHandle = handle; 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); obj->base.handle = handle; obj->base.flags |= SC_PKCS11_OBJECT_SEEN; obj->refcount++; /* Add related objects * XXX prevent infinite recursion when a card specifies two certificates * referring to each other. */ obj->base.flags |= SC_PKCS11_OBJECT_RECURS; switch (__p15_type(obj)) { case SC_PKCS15_TYPE_PRKEY_RSA: case SC_PKCS15_TYPE_PRKEY_GOSTR3410: case SC_PKCS15_TYPE_PRKEY_EC: 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; cert = (struct pkcs15_cert_object*) obj2; if ((struct pkcs15_any_object*)(cert->cert_prvkey) != obj) continue; pkcs15_add_object(slot, obj2, NULL); } } break; case SC_PKCS15_TYPE_CERT_X509: pkcs15_add_object(slot, (struct pkcs15_any_object *) obj->related_pubkey, NULL); pkcs15_add_object(slot, (struct pkcs15_any_object *) obj->related_cert, NULL); break; } 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, struct sc_pkcs15_object *auth, struct sc_app_info *app_info) { struct pkcs15_slot_data *fw_data; struct sc_pkcs15_auth_info *pin_info = NULL; 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) slot->token_info.flags |= CKF_USER_PIN_INITIALIZED; if ((p15card->card->reader->capabilities & SC_READER_CAP_PIN_PAD) || (p15card->card->caps & SC_CARD_CAP_PROTECTED_AUTHENTICATION_PATH)) slot->token_info.flags |= CKF_PROTECTED_AUTHENTICATION_PATH; 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; } fw_data->auth_obj = auth; if (auth != NULL) { pin_info = (struct sc_pkcs15_auth_info*) auth->data; if (pin_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) { pin_info = NULL; } else { 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; } } if (pin_info) { slot->token_info.ulMaxPinLen = pin_info->attrs.pin.max_length; slot->token_info.ulMinPinLen = pin_info->attrs.pin.min_length; } else { /* 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 slot 0x%lx with token %*s", slot->id, (int)sizeof(slot->token_info.label), slot->token_info.label); } static CK_RV pkcs15_create_slot(struct sc_pkcs11_card *p11card, struct pkcs15_fw_data *fw_data, struct sc_pkcs15_object *auth, struct sc_app_info *app_info, struct sc_pkcs11_slot **out) { struct sc_pkcs11_slot *slot = NULL; CK_RV rv; rv = slot_allocate(&slot, p11card); if (rv != CKR_OK) return rv; /* There's a token in this slot */ slot->slot_info.flags |= CKF_TOKEN_PRESENT; /* 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; } static int _pkcs15_create_typed_objects(struct pkcs15_fw_data *fw_data) { int rv; rv = pkcs15_create_pkcs11_objects(fw_data, SC_PKCS15_TYPE_PRKEY_RSA, "RSA private key", __pkcs15_create_prkey_object); if (rv < 0) return rv; rv = pkcs15_create_pkcs11_objects(fw_data, SC_PKCS15_TYPE_PUBKEY_RSA, "RSA public key", __pkcs15_create_pubkey_object); if (rv < 0) return rv; rv = pkcs15_create_pkcs11_objects(fw_data, SC_PKCS15_TYPE_PRKEY_EC, "EC private key", __pkcs15_create_prkey_object); if (rv < 0) return rv; rv = pkcs15_create_pkcs11_objects(fw_data, SC_PKCS15_TYPE_PUBKEY_EC, "EC public key", __pkcs15_create_pubkey_object); 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) return rv; rv = pkcs15_create_pkcs11_objects(fw_data, SC_PKCS15_TYPE_PUBKEY_GOSTR3410, "GOSTR3410 public key", __pkcs15_create_pubkey_object); if (rv < 0) return rv; rv = pkcs15_create_pkcs11_objects(fw_data, SC_PKCS15_TYPE_CERT_X509, "certificate", __pkcs15_create_cert_object); if (rv < 0) return rv; rv = pkcs15_create_pkcs11_objects(fw_data, SC_PKCS15_TYPE_DATA_OBJECT, "data object", __pkcs15_create_data_object); 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); return rv; } int _is_slot_auth_object(struct sc_pkcs15_auth_info *pin_info) { /* Ignore all but PIN authentication objects */ if (pin_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return 0; /* Ignore any non-authentication PINs */ if ((pin_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN) != 0) return 0; /* Ignore unblocking pins */ if (!sc_pkcs11_conf.create_puk_slot) if (pin_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN) return 0; return 1; } int slot_get_logged_in_state(struct sc_pkcs11_slot *slot) { int logged_in = SC_PIN_STATE_UNKNOWN; struct pkcs15_fw_data *fw_data = NULL; struct sc_pkcs15_card *p15card = NULL; 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); } else { pin_obj = slot_data_auth(slot->fw_data); } if (!pin_obj) goto out; pin_info = (struct sc_pkcs15_auth_info *)pin_obj->data; if (!pin_info) goto out; sc_pkcs15_get_pin_info(p15card, pin_obj); logged_in = pin_info->logged_in; 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, char *label) { struct sc_pkcs15_object *out = NULL; int rv = SC_ERROR_OBJECT_NOT_FOUND; /* 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); if (rv) rv = sc_pkcs15_find_pin_by_flags(p15card, SC_PKCS15_PIN_TYPE_FLAGS_PIN_LOCAL, SC_PKCS15_PIN_TYPE_FLAGS_MASK, NULL, &out); } else if (!strcmp(name, "SignPIN")) { int idx = 0; /* Get the 'global' user PIN */ rv = sc_pkcs15_find_pin_by_flags(p15card, SC_PKCS15_PIN_TYPE_FLAGS_PIN_GLOBAL, SC_PKCS15_PIN_TYPE_FLAGS_MASK, NULL, &out); if (!rv) { /* Global (user) PIN exists, get the local one -- sign PIN */ rv = sc_pkcs15_find_pin_by_flags(p15card, SC_PKCS15_PIN_TYPE_FLAGS_PIN_LOCAL, SC_PKCS15_PIN_TYPE_FLAGS_MASK, NULL, &out); } else { /* No global PIN, try to get first local one -- user PIN */ rv = sc_pkcs15_find_pin_by_flags(p15card, SC_PKCS15_PIN_TYPE_FLAGS_PIN_LOCAL, SC_PKCS15_PIN_TYPE_FLAGS_MASK, &idx, &out); if (!rv) { /* User PIN is local, try to get the second local -- sign PIN */ idx++; rv = sc_pkcs15_find_pin_by_flags(p15card, SC_PKCS15_PIN_TYPE_FLAGS_PIN_LOCAL, SC_PKCS15_PIN_TYPE_FLAGS_MASK, &idx, &out); } } } else if (!strcmp(name, "UserPUK")) { /* Get the 'global' PUK; if no, get the 'local' one */ rv = sc_pkcs15_find_pin_by_flags(p15card, SC_PKCS15_PIN_TYPE_FLAGS_PUK_GLOBAL, SC_PKCS15_PIN_TYPE_FLAGS_MASK, NULL, &out); if (rv) rv = sc_pkcs15_find_pin_by_flags(p15card, SC_PKCS15_PIN_TYPE_FLAGS_PUK_LOCAL, SC_PKCS15_PIN_TYPE_FLAGS_MASK, NULL, &out); } else if (!strcmp(name, "SignPUK")) { /* TODO: Sign PUK to be defined */ } else if (!strcmp(name, "SoPIN")) { rv = sc_pkcs15_find_pin_by_flags(p15card, SC_PKCS15_PIN_TYPE_FLAGS_SOPIN, SC_PKCS15_PIN_TYPE_FLAGS_SOPIN, NULL, &out); } 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)); for (i=0; i < fw_data->num_objects; i++) { struct pkcs15_any_object *obj = fw_data->objects[i]; /* "Fake" objects we've generated */ if (__p15_type(obj) == (unsigned int)-1) continue; /* Some objects have an auth_id even though they are * not private. Just ignore those... */ if (!(obj->p15_object->flags & SC_PKCS15_CO_FLAG_PRIVATE)) continue; sc_log(context, "ObjID(%p,%.*s,%x):%s", obj, (int) sizeof obj->p15_object->label, obj->p15_object->label, obj->p15_object->type, sc_pkcs15_print_id(&obj->p15_object->auth_id)); if (!sc_pkcs15_compare_id(&pin_info->auth_id, &obj->p15_object->auth_id)) { sc_log(context, "Ignoring object %d", i); continue; } if (is_privkey(obj)) { sc_log(context, "Slot:%p, obj:%p Adding private key %d to PIN '%.*s'", slot, obj, i, (int) sizeof pin_obj->label, pin_obj->label); pkcs15_add_object(slot, obj, NULL); } else if (is_data(obj)) { sc_log(context, "Slot:%p Adding data object %d to PIN '%.*s'", slot, i, (int) sizeof pin_obj->label, pin_obj->label); pkcs15_add_object(slot, obj, NULL); } 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 skipped", slot, i); continue; } if (move_to_fw && move_to_fw != fw_data && move_to_fw->num_objects < MAX_OBJECTS) { int tail = fw_data->num_objects - i - 1; move_to_fw->objects[move_to_fw->num_objects++] = obj; if (tail) memcpy(&fw_data->objects[i], &fw_data->objects[i + 1], sizeof(fw_data->objects[0]) * tail); i--; 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) return; sc_log(context, "%i public objects to process", fw_data->num_objects); for (i=0; i < fw_data->num_objects; i++) { struct pkcs15_any_object *obj = fw_data->objects[i]; /* "Fake" objects we've generated */ if (__p15_type(obj) == (unsigned int)-1) continue; /* Ignore seen object */ if (obj->base.flags & SC_PKCS11_OBJECT_SEEN) continue; /* Ignore 'private' object */ if (obj->p15_object->flags & SC_PKCS15_CO_FLAG_PRIVATE) { /* If we found some non-accessible public object, * we can no longer claim Public Ceritificate 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 writing for example. * See bug issue #291 * 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); 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) { struct pkcs15_fw_data *fw_data = NULL; 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; CK_RV rv; int rc, i, idx; scconf_block *conf_block = NULL; char *user_pin = NULL, *sign_pin = NULL; 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) { 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", user_pin); if (cs_flags & SC_PKCS11_SLOT_FOR_PIN_SIGN) 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 */ 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: * - 'UserPIN' cannot be identified (VT: for some cards with incomplete PIN flags); * - configuration impose to create slot for all PINs. */ if (!auth_user_pin || cs_flags & SC_PKCS11_SLOT_CREATE_ALL) { struct sc_pkcs15_object *auths[MAX_OBJECTS]; int auth_count; memset(auths, 0, sizeof(auths)); /* Get authentication PKCS#15 objects present in the associated on-card application */ 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; /* Check if a slot could be created with this PIN */ if (!_is_slot_auth_object(pin_info)) continue; sc_log(context, "Found authentication object '%.*s'", (int) sizeof auths[i]->label, auths[i]->label); rv = pkcs15_create_slot(p11card, fw_data, auths[i], app_info, &islot); if (rv != CKR_OK) return CKR_OK; /* no more slots available for this card */ islot->fw_data_idx = idx; _add_pin_related_objects(islot, auths[i], fw_data, NULL); /* Get slot to which the public objects will be associated */ if (!slot && !auth_user_pin) slot = islot; else if (!slot && auth_user_pin && auth_user_pin == auths[i]) slot = islot; } } 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 */ sc_log(context, "Create slot for User PIN '%.*s'", (int) sizeof auth_user_pin->label, auth_user_pin->label); rv = pkcs15_create_slot(p11card, fw_data, auth_user_pin, app_info, &slot); if (rv != CKR_OK) return CKR_OK; /* no more slots available for this card */ slot->fw_data_idx = idx; _add_pin_related_objects(slot, auth_user_pin, fw_data, NULL); } if (auth_sign_pin && (cs_flags & SC_PKCS11_SLOT_FOR_PIN_SIGN)) { /* Only Sign PIN slot needs to be exposed */ sc_log(context, "Create slot for Sign PIN '%.*s'", (int) sizeof auth_sign_pin->label, auth_sign_pin->label); rv = pkcs15_create_slot(p11card, fw_data, auth_sign_pin, app_info, &sign_slot); if (rv != CKR_OK) return CKR_OK; /* no more slots available for this card */ sign_slot->fw_data_idx = idx; _add_pin_related_objects(sign_slot, auth_sign_pin, fw_data, NULL); } if (!slot && sign_slot) slot = sign_slot; } if (!slot && (cs_flags == SC_PKCS11_SLOT_CREATE_ALL)) { sc_log(context, "Now create slot without AUTH object"); pkcs15_create_slot(p11card, fw_data, NULL, app_info, &slot); sc_log(context, "Created slot without AUTH object: %p", slot); } if (slot) { sc_log(context, "Add public objects to slot %p", slot); _add_public_objects(slot, fw_data); } sc_log(context, "All tokens created"); return CKR_OK; } static CK_RV pkcs15_release_token(struct sc_pkcs11_card *p11card, void *fw_token) { sc_log(context, "pkcs15_release_token() not implemented"); free(fw_token); return CKR_FUNCTION_REJECTED; } 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; 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) { case CKU_USER: auth_object = slot_data_auth(slot->fw_data); if (auth_object == NULL) return CKR_USER_PIN_NOT_INITIALIZED; break; case CKU_SO: /* A card with no SO PIN is treated as if no SO login * is required */ rc = sc_pkcs15_find_so_pin(p15card, &auth_object); sc_log(context, "pkcs15-login: find SO PIN: rc %i", rc); /* If there's no SO PIN on the card, silently * accept any PIN, and lock the card if required */ if (rc == SC_ERROR_OBJECT_NOT_FOUND) { rc = 0; if (sc_pkcs11_conf.lock_login) rc = lock_card(fw_data); if (sc_pkcs11_conf.pin_unblock_style == SC_PKCS11_PIN_UNBLOCK_SO_LOGGED_INITPIN) { 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; } } } sc_log(context, "No SOPIN found; returns %d", rc); return sc_to_cryptoki_error(rc, "C_Login"); } else if (rc < 0) { return sc_to_cryptoki_error(rc, "C_Login"); } break; case CKU_CONTEXT_SPECIFIC: /* * A session should already be open for user or SO * All we need to do is authenticate to the card * using the correct auth_object. * TODO: handle the CK_SO case */ sc_log(context, "context specific login %d", slot->login_user); if (slot->login_user == CKU_USER) { auth_object = slot_data_auth(slot->fw_data); if (auth_object == NULL) return CKR_USER_PIN_NOT_INITIALIZED; break; } /* TODO looks like this was never executed, * And even if it was, why the lock as a session * should already be open and the card locked. */ /* For a while, used only to unblock User PIN. */ rc = 0; if (sc_pkcs11_conf.lock_login) rc = lock_card(fw_data); sc_log(context, "context specific login returns %d", rc); return sc_to_cryptoki_error(rc, "C_Login"); default: return CKR_USER_TYPE_INVALID; } pin_info = (struct sc_pkcs15_auth_info *) auth_object->data; if (pin_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return CKR_FUNCTION_REJECTED; 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. * Otherwise an attacker could perform some crypto operation * after we've authenticated with the card */ /* Context specific login is not real login but only a * reassertion of the PIN to the card. * And we don't want to do any extra operations to the card * that could invalidate the assertion of the pin * before the crypto operation that requires the assertion */ if (userType != CKU_CONTEXT_SPECIFIC) { if (sc_pkcs11_conf.lock_login && (rc = lock_card(fw_data)) < 0) { return sc_to_cryptoki_error(rc, "C_Login"); } } 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) return sc_to_cryptoki_error(rc, "C_Login"); if (userType == CKU_USER) { sc_pkcs15_object_t *p15_obj = p15card->obj_list; sc_pkcs15_search_key_t sk; sc_log(context, "Check if pkcs15 object list can be completed."); /* Ensure non empty list */ if (p15_obj == NULL) return CKR_OK; /* Select last object in list */ while(p15_obj->next) p15_obj = p15_obj->next; /* Trigger enumeration of EF.XXX files */ memset(&sk, 0, sizeof(sk)); sk.class_mask = SC_PKCS15_SEARCH_CLASS_PRKEY | SC_PKCS15_SEARCH_CLASS_PUBKEY | SC_PKCS15_SEARCH_CLASS_CERT | SC_PKCS15_SEARCH_CLASS_DATA; sc_pkcs15_search_objects(p15card, &sk, NULL, 0); /* Iterate over newly discovered objects */ while(p15_obj->next) { struct pkcs15_any_object *fw_obj; p15_obj = p15_obj->next; if (!sc_pkcs15_compare_id(&pin_info->auth_id, &p15_obj->auth_id)) continue; switch (p15_obj->type & SC_PKCS15_TYPE_CLASS_MASK) { case SC_PKCS15_TYPE_PRKEY: __pkcs15_create_prkey_object(fw_data, p15_obj, &fw_obj); break; case SC_PKCS15_TYPE_PUBKEY: __pkcs15_create_pubkey_object(fw_data, p15_obj, &fw_obj); break; case SC_PKCS15_TYPE_CERT: __pkcs15_create_cert_object(fw_data, p15_obj, &fw_obj); break; case SC_PKCS15_TYPE_DATA_OBJECT: __pkcs15_create_data_object(fw_data, p15_obj, &fw_obj); break; default: continue; } sc_log(context, "new object found: type=0x%03X", p15_obj->type); pkcs15_add_object(slot, fw_obj, NULL); } } return CKR_OK; } /** * 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"); 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 */ if (rc == SC_ERROR_NOT_SUPPORTED) rc = SC_SUCCESS; if (rc != SC_SUCCESS) ret = sc_to_cryptoki_error(rc, "C_Logout"); if (sc_pkcs11_conf.lock_login) { rc = unlock_card(fw_data); if (rc != SC_SUCCESS) ret = sc_to_cryptoki_error(rc, "C_Logout"); } #endif /* TODO DEE free any session objects ? */ return ret; } static CK_RV pkcs15_change_pin(struct sc_pkcs11_slot *slot, CK_CHAR_PTR pOldPin, CK_ULONG ulOldLen, CK_CHAR_PTR pNewPin, CK_ULONG ulNewLen) { struct sc_pkcs11_card *p11card = slot->p11card; struct sc_pkcs15_card *p15card = NULL; struct pkcs15_fw_data *fw_data = NULL; struct sc_pkcs15_auth_info *auth_info = NULL; struct sc_pkcs15_object *pin_obj = NULL; 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); sc_log(context, "pkcs15-login: find SO PIN: rc %i", rc); } else { pin_obj = slot_data_auth(slot->fw_data); } if (!pin_obj) return CKR_USER_PIN_NOT_INITIALIZED; auth_info = (struct sc_pkcs15_auth_info *)pin_obj->data; if (!auth_info) 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 (pNewPin && (ulNewLen < auth_info->attrs.pin.min_length || ulNewLen > auth_info->attrs.pin.max_length)) { return CKR_PIN_LEN_RANGE; } 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) { if (sc_pkcs11_conf.pin_unblock_style != SC_PKCS11_PIN_UNBLOCK_SCONTEXT_SETPIN) { sc_log(context, "PIN unlock is not allowed with CKU_CONTEXT_SPECIFIC login"); return CKR_FUNCTION_NOT_SUPPORTED; } rc = sc_pkcs15_unblock_pin(fw_data->p15_card, pin_obj, pOldPin, ulOldLen, pNewPin, ulNewLen); } 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 { sc_log(context, "cannot change PIN: non supported login type: %i", login_user); return CKR_FUNCTION_NOT_SUPPORTED; } sc_log(context, "PIN change returns %d", rc); return sc_to_cryptoki_error(rc, "C_SetPIN"); } #ifdef USE_PKCS15_INIT static CK_RV pkcs15_initialize(struct sc_pkcs11_slot *slot, void *ptr, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen, CK_UTF8CHAR_PTR pLabel) { struct sc_pkcs11_card *p11card = slot->p11card; struct sc_cardctl_pkcs11_init_token args; scconf_block *conf_block = NULL; int rc, enable_InitToken = 0; CK_RV rv; sc_log(context, "Get 'enable-InitToken' card configuration option"); 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; args.so_pin_len = ulPinLen; args.label = (const char *) pLabel; sc_log(context, "Try card specific token initialize procedure"); rc = sc_card_ctl(p11card->card, SC_CARDCTL_PKCS11_INIT_TOKEN, &args); if (rc == SC_ERROR_NOT_SUPPORTED && enable_InitToken) { struct sc_profile *profile = NULL; struct pkcs15_fw_data *fw_data = NULL; struct sc_pkcs15_card *p15card = NULL; sc_log(context, "Using generic token initialize procedure"); 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; rc = sc_lock(p11card->card); if (rc < 0) return sc_to_cryptoki_error(rc, "C_InitToken"); rc = sc_pkcs15init_bind(p11card->card, "pkcs15", NULL, NULL, &profile); if (rc < 0) { sc_log(context, "pkcs15init bind error %i", rc); sc_unlock(p11card->card); return sc_to_cryptoki_error(rc, "C_InitToken"); } rc = sc_pkcs15init_finalize_profile(p11card->card, profile, NULL); if (rc) { sc_log(context, "finalize profile error %i", rc); return sc_to_cryptoki_error(rc, "C_InitToken"); } sc_log(context, "set pkcs15init callbacks"); pkcs15init_sopin = (char *)pPin; pkcs15init_sopin_len = ulPinLen; sc_pkcs15init_set_callbacks(&pkcs15init_callbacks); if (p15card) { sc_log(context, "pkcs15init erase card"); sc_pkcs15init_erase_card(p15card, profile, NULL); sc_log(context, "pkcs15init unbind"); sc_pkcs15init_unbind(profile); rc = sc_pkcs15init_bind(p11card->card, "pkcs15", NULL, NULL, &profile); if (rc < 0) { sc_log(context, "pkcs15init bind error %i", rc); sc_pkcs15init_set_callbacks(NULL); sc_unlock(p11card->card); return sc_to_cryptoki_error(rc, "C_InitToken"); } rc = sc_pkcs15init_finalize_profile(p11card->card, profile, NULL); if (rc) { sc_pkcs15init_set_callbacks(NULL); sc_log(context, "Cannot finalize profile: %i", rc); return sc_to_cryptoki_error(rc, "C_InitToken"); } } else { sc_log(context, "No erase for the non-initialized card"); } if (!rc) { struct sc_pkcs15init_initargs init_args; memset(&init_args, 0, sizeof(init_args)); init_args.so_pin = pPin; init_args.so_pin_len = ulPinLen; init_args.label = (char *)pLabel; sc_log(context, "pkcs15init: create application on '%s' card", p11card->card->name); rc = sc_pkcs15init_add_app(p11card->card, profile, &init_args); sc_log(context, "pkcs15init: create application returns %i", rc); } pkcs15init_sopin = NULL; pkcs15init_sopin_len = 0; sc_log(context, "pkcs15init: unset callbacks"); sc_pkcs15init_set_callbacks(NULL); sc_log(context, "pkcs15init: unbind"); sc_pkcs15init_unbind(profile); sc_unlock(p11card->card); } if (rc < 0) { sc_log(context, "init token error %i", rc); return sc_to_cryptoki_error(rc, "C_InitToken"); } rv = card_removed(p11card->reader); if (rv != CKR_OK) { sc_log(context, "remove card error 0x%lX", rv); return rv; } rv = card_detect_all(); if (rv != CKR_OK) { sc_log(context, "detect all card error 0x%lX", rv); return rv; } return CKR_OK; } static CK_RV pkcs15_init_pin(struct sc_pkcs11_slot *slot, CK_CHAR_PTR pPin, CK_ULONG ulPinLen) { struct sc_pkcs11_card *p11card = slot->p11card; struct pkcs15_fw_data *fw_data = NULL; struct sc_pkcs15init_pinargs args; struct sc_profile *profile = NULL; struct sc_pkcs15_object *auth_obj = NULL; struct sc_pkcs15_auth_info *auth_info = NULL; struct sc_cardctl_pkcs11_init_pin p11args; int rc; memset(&p11args, 0, sizeof(p11args)); 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) return CKR_OK; return sc_to_cryptoki_error(rc, "C_InitPin"); } sc_log(context, "Init PIN: pin %p:%lu; unblock style %i", pPin, ulPinLen, sc_pkcs11_conf.pin_unblock_style); 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_InitPin"); 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) rc = sc_pkcs15_unblock_pin(fw_data->p15_card, auth_obj, fw_data->user_puk, fw_data->user_puk_len, pPin, ulPinLen); else /* FIXME (VT): Actually sc_pkcs15_unblock_pin() do not accepts zero length PUK. * Something like sc_pkcs15_set_pin() should be introduced. * For a while, use the 'libopensc' API to set PIN. */ rc = sc_reset_retry_counter(fw_data->p15_card->card, SC_AC_CHV, auth_info->attrs.pin.reference, NULL, 0, pPin, ulPinLen); return sc_to_cryptoki_error(rc, "C_InitPIN"); } rc = sc_lock(p11card->card); if (rc < 0) return sc_to_cryptoki_error(rc, "C_InitPIN"); rc = sc_pkcs15init_bind(p11card->card, "pkcs15", NULL, NULL, &profile); if (rc < 0) { sc_unlock(p11card->card); return sc_to_cryptoki_error(rc, "C_InitPIN"); } rc = sc_pkcs15init_finalize_profile(p11card->card, profile, NULL); if (rc != CKR_OK) { sc_log(context, "Cannot finalize profile: %i", rc); return sc_to_cryptoki_error(rc, "C_InitPIN"); } memset(&args, 0, sizeof(args)); args.label = "User PIN"; args.pin = pPin; args.pin_len = ulPinLen; rc = sc_pkcs15init_store_pin(fw_data->p15_card, profile, &args); sc_pkcs15init_unbind(profile); sc_unlock(p11card->card); if (rc < 0) return sc_to_cryptoki_error(rc, "C_InitPIN"); rc = sc_pkcs15_find_pin_by_auth_id(fw_data->p15_card, &args.auth_id, &auth_obj); if (rc < 0) return sc_to_cryptoki_error(rc, "C_InitPIN"); /* Re-initialize the slot */ free(slot->fw_data); pkcs15_init_slot(fw_data->p15_card, slot, auth_obj, slot->app_info); return CKR_OK; } static unsigned long pkcs15_check_bool_cka(CK_ATTRIBUTE_PTR attr, unsigned long flag) { if (attr->ulValueLen != sizeof(CK_BBOOL) || !attr->pValue) return 0; if (*((CK_BBOOL *)attr->pValue)) return flag; return 0; } static CK_RV pkcs15_create_private_key(struct sc_pkcs11_slot *slot, struct sc_profile *profile, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phObject) { struct sc_pkcs11_card *p11card = slot->p11card; struct pkcs15_fw_data *fw_data = NULL; struct sc_pkcs15init_prkeyargs args; struct pkcs15_any_object *key_any_obj = NULL; struct sc_pkcs15_object *key_obj = NULL; struct sc_pkcs15_auth_info *pin = NULL; CK_KEY_TYPE key_type; struct sc_pkcs15_prkey_rsa *rsa = NULL; struct sc_pkcs15_prkey_gostr3410 *gost = NULL; 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) args.auth_id = pin->auth_id; /* Get the key type */ rv = attr_find(pTemplate, ulCount, CKA_KEY_TYPE, &key_type, NULL); if (rv != CKR_OK) return rv; switch (key_type) { case CKK_RSA: args.key.algorithm = SC_ALGORITHM_RSA; rsa = &args.key.u.rsa; break; case CKK_GOSTR3410: set_gost3410_params(&args, NULL, pTemplate, ulCount, NULL, 0); args.key.algorithm = SC_ALGORITHM_GOSTR3410; gost = &args.key.u.gostr3410; break; case CKK_EC_EDWARDS: args.key.algorithm = SC_ALGORITHM_EDDSA; /* TODO */ return CKR_ATTRIBUTE_VALUE_INVALID; case CKK_EC_MONTGOMERY: args.key.algorithm = SC_ALGORITHM_XEDDSA; /* TODO */ return CKR_ATTRIBUTE_VALUE_INVALID; case CKK_EC: args.key.algorithm = SC_ALGORITHM_EC; ec = &args.key.u.ec; break; default: return CKR_ATTRIBUTE_VALUE_INVALID; } while (ulCount--) { CK_ATTRIBUTE_PTR attr = pTemplate++; sc_pkcs15_bignum_t *bn = NULL; switch (attr->type) { /* Skip attrs we already know or don't care for */ case CKA_CLASS: case CKA_KEY_TYPE: case CKA_MODULUS_BITS: case CKA_PRIVATE: break; case CKA_LABEL: args.label = set_cka_label(attr, label); break; case CKA_ID: args.id.len = sizeof(args.id.value); rv = attr_extract(attr, args.id.value, &args.id.len); if (rv != CKR_OK) goto out; break; case CKA_MODULUS: bn = &rsa->modulus; break; case CKA_PUBLIC_EXPONENT: bn = &rsa->exponent; break; case CKA_PRIVATE_EXPONENT: bn = &rsa->d; break; case CKA_PRIME_1: bn = &rsa->p; break; case CKA_PRIME_2: bn = &rsa->q; break; case CKA_VALUE: if (key_type == CKK_GOSTR3410) bn = &gost->d; if (key_type == CKK_EC) 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) 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); break; case CKA_SIGN_RECOVER: args.usage |= pkcs15_check_bool_cka(attr, SC_PKCS15_PRKEY_USAGE_SIGNRECOVER); break; case CKA_DECRYPT: args.usage |= pkcs15_check_bool_cka(attr, SC_PKCS15_PRKEY_USAGE_DECRYPT); break; case CKA_UNWRAP: args.usage |= pkcs15_check_bool_cka(attr, SC_PKCS15_PRKEY_USAGE_UNWRAP); break; 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; } if (bn) { if (attr->ulValueLen > 1024) { return CKR_ATTRIBUTE_VALUE_INVALID; } bn->len = attr->ulValueLen; bn->data = (u8 *) attr->pValue; } } if (key_type == CKK_RSA) { if (!rsa->modulus.len || !rsa->exponent.len || !rsa->d.len || !rsa->p.len || !rsa->q.len) { sc_log(context, "Template to store the RSA key is incomplete"); rv = CKR_TEMPLATE_INCOMPLETE; goto out; } } else if (key_type == CKK_GOSTR3410) { if (!gost->d.len) { sc_log(context, "Template to store the GOST key is incomplete"); return CKR_ATTRIBUTE_VALUE_INVALID; } /* CKA_VALUE arrives in little endian form. pkcs15init framework expects it in a big endian one. */ rc = sc_mem_reverse(gost->d.data, gost->d.len); if (rc != SC_SUCCESS) { rv = sc_to_cryptoki_error(rc, "C_CreateObject"); goto out; } } else if (key_type == CKK_EC) { 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) { /* allocated above */ free(ec->params.der.value); /* in sc_pkcs15_fix_ec_parameters() */ free(ec->params.named_curve); } if (rc < 0) { rv = sc_to_cryptoki_error(rc, "C_CreateObject"); goto out; } /* Create a new pkcs11 object for it */ __pkcs15_create_prkey_object(fw_data, key_obj, &key_any_obj); pkcs15_add_object(slot, key_any_obj, phObject); rv = CKR_OK; out: return rv; } /* * 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 the attributes as needed. * . */ static CK_RV pkcs15_create_secret_key(struct sc_pkcs11_slot *slot, struct sc_profile *profile, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phObject) { struct sc_pkcs11_card *p11card = slot->p11card; struct pkcs15_fw_data *fw_data = NULL; 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; 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); if (rv != CKR_OK) return rv; /* CKA_TOKEN defaults to false */ rv = attr_find(pTemplate, ulCount, CKA_TOKEN, &_token, NULL); if (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) { 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; } while (ulCount--) { CK_ATTRIBUTE_PTR attr = pTemplate++; switch (attr->type) { /* Skip attrs we already know or don't care for */ case CKA_CLASS: case CKA_KEY_TYPE: case CKA_MODULUS_BITS: case CKA_PRIVATE: break; case CKA_LABEL: args.label = set_cka_label(attr, label); break; case CKA_ID: args.id.len = sizeof(args.id.value); rv = attr_extract(attr, args.id.value, &args.id.len); if (rv != CKR_OK) goto out; break; case CKA_VALUE_LEN: attr_extract(attr, &args.value_len, NULL); break; case CKA_VALUE: if (attr->pValue) { 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: args.usage |= pkcs15_check_bool_cka(attr, SC_PKCS15_PRKEY_USAGE_DECRYPT); break; case CKA_ENCRYPT: args.usage |= pkcs15_check_bool_cka(attr, SC_PKCS15_PRKEY_USAGE_ENCRYPT); break; case CKA_WRAP: args.usage |= pkcs15_check_bool_cka(attr, SC_PKCS15_PRKEY_USAGE_WRAP); break; 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; } } /* If creating a PKCS#11 session object, i.e. one that is only in memory */ 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) */ key_obj = calloc(1, sizeof(sc_pkcs15_object_t)); if (key_obj == NULL) { rv = CKR_HOST_MEMORY; goto out; } temp_object = TRUE; key_obj->type = SC_PKCS15_TYPE_SKEY; if (args.id.len) memcpy(key_obj->label, args.id.value, args.id.len); key_obj->flags = 2; /* TODO not sure what these mean */ 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 = (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(_token == FALSE) args.session_object = 1; /* store the object on card for duration of the session. */ 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 */ __pkcs15_create_secret_key_object(fw_data, key_obj, &key_any_obj); pkcs15_add_object(slot, key_any_obj, phObject); rv = CKR_OK; out: 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) { struct sc_pkcs11_card *p11card = slot->p11card; struct pkcs15_fw_data *fw_data = NULL; struct sc_pkcs15init_pubkeyargs args; struct pkcs15_any_object *key_any_obj = NULL; struct sc_pkcs15_object *key_obj = NULL; struct sc_pkcs15_auth_info *pin = NULL; CK_KEY_TYPE key_type; struct sc_pkcs15_pubkey_rsa *rsa = NULL; 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) args.auth_id = pin->auth_id; /* Get the key type */ rv = attr_find(pTemplate, ulCount, CKA_KEY_TYPE, &key_type, NULL); if (rv != CKR_OK) return rv; switch (key_type) { case CKK_RSA: args.key.algorithm = SC_ALGORITHM_RSA; rsa = &args.key.u.rsa; break; case CKK_EC: args.key.algorithm = SC_ALGORITHM_EC; ec = &args.key.u.ec; break; case CKK_EC_EDWARDS: case CKK_EC_MONTGOMERY: /* TODO: -DEE Do not have real pkcs15 card with EC */ /* fall through */ default: return CKR_ATTRIBUTE_VALUE_INVALID; } while (ulCount--) { CK_ATTRIBUTE_PTR attr = pTemplate++; sc_pkcs15_bignum_t *bn = NULL; switch (attr->type) { /* Skip attrs we already know or don't care for */ case CKA_CLASS: case CKA_KEY_TYPE: case CKA_MODULUS_BITS: case CKA_PRIVATE: break; case CKA_LABEL: args.label = set_cka_label(attr, label); break; case CKA_ID: args.id.len = sizeof(args.id.value); rv = attr_extract(attr, args.id.value, &args.id.len); if (rv != CKR_OK) return rv; break; case CKA_MODULUS: bn = &rsa->modulus; break; case CKA_PUBLIC_EXPONENT: bn = &rsa->exponent; break; case CKA_VERIFY: args.usage |= pkcs15_check_bool_cka(attr, SC_PKCS15_PRKEY_USAGE_VERIFY); break; case CKA_VERIFY_RECOVER: args.usage |= pkcs15_check_bool_cka(attr, SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER); break; case CKA_ENCRYPT: args.usage |= pkcs15_check_bool_cka(attr, SC_PKCS15_PRKEY_USAGE_ENCRYPT); break; case CKA_WRAP: args.usage |= pkcs15_check_bool_cka(attr, SC_PKCS15_PRKEY_USAGE_WRAP); break; case CKA_EC_POINT: if (key_type == CKK_EC) { if (sc_pkcs15_decode_pubkey_ec(p11card->card->ctx, ec, attr->pValue, attr->ulValueLen) < 0) 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) return CKR_ATTRIBUTE_VALUE_INVALID; break; default: /* ignore unknown attrs, or flag error? */ continue; } if (bn) { if (attr->ulValueLen > 1024) return CKR_ATTRIBUTE_VALUE_INVALID; bn->len = attr->ulValueLen; bn->data = (u8 *) attr->pValue; } } if (key_type == CKK_RSA) { if (!rsa->modulus.len || !rsa->exponent.len) return CKR_TEMPLATE_INCOMPLETE; } else if (key_type == CKK_EC) { if (!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) { /* allocated above */ free(ec->params.der.value); /* in sc_pkcs15_fix_ec_parameters() */ free(ec->params.named_curve); /* in sc_pkcs15_decode_pubkey_ec() */ free(ec->ecpointQ.value); } if (rc < 0) return sc_to_cryptoki_error(rc, "C_CreateObject"); /* Create a new pkcs11 object for it */ __pkcs15_create_pubkey_object(fw_data, key_obj, &key_any_obj); pkcs15_add_object(slot, key_any_obj, phObject); return CKR_OK; } static CK_RV pkcs15_create_certificate(struct sc_pkcs11_slot *slot, struct sc_profile *profile, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phObject) { struct sc_pkcs11_card *p11card = slot->p11card; struct pkcs15_fw_data *fw_data = NULL; struct sc_pkcs15init_certargs args; struct pkcs15_any_object *cert_any_obj = NULL; struct sc_pkcs15_object *cert_obj = NULL; CK_CERTIFICATE_TYPE cert_type; CK_BBOOL bValue; 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, &cert_type, NULL); if (rv != CKR_OK) return rv; if (cert_type != CKC_X_509) return CKR_ATTRIBUTE_VALUE_INVALID; while (ulCount--) { CK_ATTRIBUTE_PTR attr = pTemplate++; switch (attr->type) { /* Skip attrs we already know or don't care for */ case CKA_CLASS: break; case CKA_PRIVATE: attr_extract(attr, &bValue, NULL); if (bValue) { rv = CKR_TEMPLATE_INCONSISTENT; goto out; } break; case CKA_LABEL: args.label = set_cka_label(attr, label); break; case CKA_ID: args.id.len = sizeof(args.id.value); rv = attr_extract(attr, args.id.value, &args.id.len); if (rv != CKR_OK) goto out; break; case CKA_VALUE: args.der_encoded.len = attr->ulValueLen; args.der_encoded.value = (u8 *) attr->pValue; break; default: /* ignore unknown attrs, or flag error? */ continue; } } if (args.der_encoded.len == 0) { rv = CKR_TEMPLATE_INCOMPLETE; goto out; } rc = sc_pkcs15init_store_certificate(fw_data->p15_card, profile, &args, &cert_obj); if (rc < 0) { rv = sc_to_cryptoki_error(rc, "C_CreateObject"); goto out; } /* Create a new pkcs11 object for it */ __pkcs15_create_cert_object(fw_data, cert_obj, &cert_any_obj); pkcs15_add_object(slot, cert_any_obj, phObject); rv = CKR_OK; out: return rv; } static CK_RV pkcs15_create_data(struct sc_pkcs11_slot *slot, struct sc_profile *profile, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phObject) { struct sc_pkcs11_card *p11card = slot->p11card; struct pkcs15_fw_data *fw_data = NULL; struct sc_pkcs15init_dataargs args; struct pkcs15_any_object *data_any_obj = NULL; struct sc_pkcs15_object *data_obj = NULL; struct sc_pkcs15_auth_info *pin = NULL; CK_BBOOL bValue; 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"); while (ulCount--) { CK_ATTRIBUTE_PTR attr = pTemplate++; switch (attr->type) { /* Skip attrs we already know or don't care for */ case CKA_CLASS: break; case CKA_PRIVATE: attr_extract(attr, &bValue, NULL); if (bValue) { pin = slot_data_auth_info(slot->fw_data); if (pin == NULL) { rv = CKR_TEMPLATE_INCOMPLETE; goto out; } args.auth_id = pin->auth_id; } break; case CKA_LABEL: args.label = set_cka_label(attr, label); break; case CKA_ID: args.id.len = sizeof(args.id.value); rv = attr_extract(attr, args.id.value, &args.id.len); if (rv != CKR_OK) goto out; break; case CKA_APPLICATION: args.app_label = (char *) attr->pValue; break; case CKA_OBJECT_ID: if (sc_asn1_decode_object_id(attr->pValue, attr->ulValueLen, &args.app_oid)) { rv = CKR_ATTRIBUTE_VALUE_INVALID; goto out; } break; case CKA_VALUE: args.der_encoded.len = attr->ulValueLen; args.der_encoded.value = (u8 *) attr->pValue; break; default: /* ignore unknown attrs, or flag error? */ continue; } } 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"); goto out; } /* Create a new pkcs11 object for it */ __pkcs15_create_data_object(fw_data, data_obj, &data_any_obj); pkcs15_add_object(slot, data_any_obj, phObject); rv = CKR_OK; out: return rv; } static CK_RV pkcs15_create_object(struct sc_pkcs11_slot *slot, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phObject) { struct sc_pkcs11_card *p11card = slot->p11card; struct pkcs15_fw_data *fw_data = NULL; struct sc_profile *profile = NULL; CK_OBJECT_CLASS _class; CK_BBOOL _token = FALSE; 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) { 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 compatibility, will default to TRUE */ /* Dont need profile id 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); if (rc < 0) return sc_to_cryptoki_error(rc, "C_CreateObject"); /* Bind the profile */ rc = sc_pkcs15init_bind(p11card->card, "pkcs15", NULL, slot->app_info, &profile); if (rc < 0) { sc_unlock(p11card->card); return sc_to_cryptoki_error(rc, "C_CreateObject"); } if (slot->app_info) aid = &slot->app_info->aid; rc = sc_pkcs15init_finalize_profile(p11card->card, profile, aid); if (rc != CKR_OK) { sc_log(context, "Cannot finalize profile: %i", rc); sc_unlock(p11card->card); return sc_to_cryptoki_error(rc, "C_CreateObject"); } sc_pkcs15init_set_p15card(profile, fw_data->p15_card); } switch (_class) { case CKO_PRIVATE_KEY: rv = pkcs15_create_private_key(slot, profile, pTemplate, ulCount, phObject); break; case CKO_PUBLIC_KEY: rv = pkcs15_create_public_key(slot, profile, pTemplate, ulCount, phObject); break; case CKO_CERTIFICATE: rv = pkcs15_create_certificate(slot, profile, pTemplate, ulCount, phObject); break; case CKO_DATA: rv = pkcs15_create_data(slot, profile, pTemplate, ulCount, phObject); break; case CKO_SECRET_KEY: rv = pkcs15_create_secret_key(slot, profile, pTemplate, ulCount, phObject); break; default: rv = CKR_FUNCTION_NOT_SUPPORTED; } 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); } return rv; } static CK_RV get_X509_usage_privk(CK_ATTRIBUTE_PTR pTempl, CK_ULONG ulCount, unsigned long *x509_usage) { CK_ULONG i; for (i = 0; i < ulCount; i++) { CK_ATTRIBUTE_TYPE typ = pTempl[i].type; CK_BBOOL *val = (CK_BBOOL *) pTempl[i].pValue; if (val == NULL) continue; if (typ == CKA_SIGN && *val) *x509_usage |= SC_PKCS15INIT_X509_DIGITAL_SIGNATURE; if (typ == CKA_UNWRAP && *val) *x509_usage |= SC_PKCS15INIT_X509_KEY_ENCIPHERMENT; if (typ == CKA_DECRYPT && *val) *x509_usage |= SC_PKCS15INIT_X509_DATA_ENCIPHERMENT; if (typ == CKA_DERIVE && *val) *x509_usage |= SC_PKCS15INIT_X509_KEY_AGREEMENT; if (typ == CKA_OPENSC_NON_REPUDIATION && *val) *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); return CKR_ATTRIBUTE_TYPE_INVALID; } } return CKR_OK; } static CK_RV get_X509_usage_pubk(CK_ATTRIBUTE_PTR pTempl, CK_ULONG ulCount, unsigned long *x509_usage) { CK_ULONG i; for (i = 0; i < ulCount; i++) { CK_ATTRIBUTE_TYPE typ = pTempl[i].type; CK_BBOOL *val = (CK_BBOOL *) pTempl[i].pValue; if (val == NULL) continue; if (typ == CKA_VERIFY && *val) *x509_usage |= SC_PKCS15INIT_X509_DIGITAL_SIGNATURE; if (typ == CKA_WRAP && *val) *x509_usage |= SC_PKCS15INIT_X509_KEY_ENCIPHERMENT; if (typ == CKA_ENCRYPT && *val) *x509_usage |= SC_PKCS15INIT_X509_DATA_ENCIPHERMENT; if (typ == CKA_DERIVE && *val) *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); return CKR_ATTRIBUTE_TYPE_INVALID; } } return CKR_OK; } static CK_RV set_gost3410_params(struct sc_pkcs15init_prkeyargs *prkey_args, struct sc_pkcs15init_pubkeyargs *pubkey_args, CK_ATTRIBUTE_PTR pPubTpl, CK_ULONG ulPubCnt, CK_ATTRIBUTE_PTR pPrivTpl, CK_ULONG ulPrivCnt) { 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, &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]); for (param_index = 0; param_index < nn; ++param_index) { if (len != gostr3410_param_oid[param_index].encoded_oid_size) continue; if (!memcmp(gost_params_encoded_oid_from_template, gostr3410_param_oid[param_index].encoded_oid, len)) break; } if (param_index == nn) return CKR_ATTRIBUTE_VALUE_INVALID; } else if (rv == CKR_TEMPLATE_INCOMPLETE) /* Default used parameters' index */ param_index = 0; else return rv; /* 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, &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]); for (hash_index = 0; hash_index < nn; ++hash_index) { if (len != gostr3410_hash_param_oid[hash_index].encoded_oid_size) continue; if (!memcmp(gost_hash_params_encoded_oid_from_template, gostr3410_hash_param_oid[hash_index].encoded_oid, len)) break; } if (hash_index == nn) return CKR_ATTRIBUTE_VALUE_INVALID; } else if (rv == CKR_TEMPLATE_INCOMPLETE) /* Default used hash parameters' index */ hash_index = 0; else return rv; /* Set params and hash oids in priv and pub keys' gostr3410 params * and set params oid_id in priv key */ if (prkey_args) { (prkey_args->params).gost.gostr3410 = gostr3410_param_oid[param_index].oid_id; memcpy(&(prkey_args->key).u.gostr3410.params.key, gostr3410_param_oid[param_index].oid, gostr3410_param_oid[param_index].oid_size); memcpy(&(prkey_args->key).u.gostr3410.params.hash, gostr3410_hash_param_oid[hash_index].oid, gostr3410_hash_param_oid[hash_index].oid_size); } if (pubkey_args) { (pubkey_args->params).gost.gostr3410 = gostr3410_param_oid[param_index].oid_id; memcpy(&(pubkey_args->key).u.gostr3410.params.key, gostr3410_param_oid[param_index].oid, gostr3410_param_oid[param_index].oid_size); memcpy(&(pubkey_args->key).u.gostr3410.params.hash, gostr3410_hash_param_oid[hash_index].oid, gostr3410_hash_param_oid[hash_index].oid_size); } return CKR_OK; } /* FIXME: check for the public exponent in public key template and use this value */ static CK_RV pkcs15_gen_keypair(struct sc_pkcs11_slot *slot, CK_MECHANISM_PTR pMechanism, CK_ATTRIBUTE_PTR pPubTpl, CK_ULONG ulPubCnt, CK_ATTRIBUTE_PTR pPrivTpl, CK_ULONG ulPrivCnt, CK_OBJECT_HANDLE_PTR phPubKey, CK_OBJECT_HANDLE_PTR phPrivKey) /* gets priv. key handle */ { struct sc_profile *profile = NULL; struct sc_pkcs11_card *p11card = slot->p11card; struct sc_pkcs15_auth_info *pin = NULL; struct sc_aid *aid = NULL; struct pkcs15_fw_data *fw_data = NULL; struct sc_pkcs15init_keygen_args keygen_args; struct sc_pkcs15init_pubkeyargs pub_args; struct sc_pkcs15_object *priv_key_obj = NULL, *pub_key_obj = NULL; struct pkcs15_any_object *priv_any_obj = NULL, *pub_any_obj = NULL; struct pkcs15_prkey_object *priv_prk_obj = NULL; struct sc_pkcs15_id id; size_t len; CK_KEY_TYPE keytype; CK_ULONG keybits = 0; char pub_label[SC_PKCS15_MAX_LABEL_SIZE]; char priv_label[SC_PKCS15_MAX_LABEL_SIZE]; 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, "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_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) return sc_to_cryptoki_error(rc, "C_GenerateKeyPair"); rc = sc_pkcs15init_bind(p11card->card, "pkcs15", NULL, slot->app_info, &profile); if (rc < 0) { sc_unlock(p11card->card); return sc_to_cryptoki_error(rc, "C_GenerateKeyPair"); } if(slot->app_info) aid = &slot->app_info->aid; rc = sc_pkcs15init_finalize_profile(p11card->card, profile, aid); if (rc != CKR_OK) { sc_log(context, "Cannot finalize profile: %i", rc); return sc_to_cryptoki_error(rc, "C_GenerateKeyPair"); } memset(&keygen_args, 0, sizeof(keygen_args)); memset(&pub_args, 0, sizeof(pub_args)); /* 1. Convert the pkcs11 attributes to pkcs15init args */ if ((pin = slot_data_auth_info(slot->fw_data)) != NULL) keygen_args.prkey_args.auth_id = pub_args.auth_id = pin->auth_id; rv = attr_find2(pPubTpl, ulPubCnt, pPrivTpl, ulPrivCnt, CKA_KEY_TYPE, &keytype, NULL); if (rv != CKR_OK && pMechanism->mechanism == CKM_RSA_PKCS_KEY_PAIR_GEN) 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) goto kpgen_done; if (keytype == CKK_GOSTR3410) { keygen_args.prkey_args.key.algorithm = SC_ALGORITHM_GOSTR3410; pub_args.key.algorithm = SC_ALGORITHM_GOSTR3410; rv = set_gost3410_params(&keygen_args.prkey_args, &pub_args, pPubTpl, ulPubCnt, pPrivTpl, ulPrivCnt); if (rv != CKR_OK) goto kpgen_done; keybits = SC_PKCS15_GOSTR3410_KEYSIZE; } else if (keytype == CKK_RSA) { /* default value (CKA_KEY_TYPE isn't set) or CKK_RSA is set */ keygen_args.prkey_args.key.algorithm = SC_ALGORITHM_RSA; pub_args.key.algorithm = SC_ALGORITHM_RSA; rv = attr_find2(pPubTpl, ulPubCnt, pPrivTpl, ulPrivCnt, CKA_MODULUS_BITS, &keybits, NULL); if (rv != CKR_OK) keybits = 1024; /* Default key size */ /* TODO: check allowed values of keybits */ } else if (keytype == CKK_EC) { 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, &ptr, &der->len); der->value = (unsigned char *) ptr; if (rv != CKR_OK) { sc_unlock(p11card->card); return rv; } keygen_args.prkey_args.key.algorithm = SC_ALGORITHM_EC; pub_args.key.algorithm = SC_ALGORITHM_EC; } else if (keytype == CKK_EC_EDWARDS) { /* TODO Validate EC_PARAMS contains curveName "edwards25519" or "edwards448" (from RFC 8032) * or id-Ed25519 or id-Ed448 (or equivalent OIDs in oId field) (from RFC 8410) * otherwise return CKR_CURVE_NOT_SUPPORTED */ keygen_args.prkey_args.key.algorithm = SC_ALGORITHM_EDDSA; pub_args.key.algorithm = SC_ALGORITHM_EDDSA; return CKR_CURVE_NOT_SUPPORTED; } else if (keytype == CKK_EC_MONTGOMERY) { /* TODO Validate EC_PARAMS contains curveName "curve25519" or "curve448" (from RFC 7748) * or id-X25519 or id-X448 (or equivalent OIDs in oId field) (from RFC 8410) * otherwise return CKR_CURVE_NOT_SUPPORTED */ keygen_args.prkey_args.key.algorithm = SC_ALGORITHM_XEDDSA; pub_args.key.algorithm = SC_ALGORITHM_XEDDSA; return CKR_CURVE_NOT_SUPPORTED; } else { /* CKA_KEY_TYPE is set, but keytype isn't correct */ rv = CKR_ATTRIBUTE_VALUE_INVALID; goto kpgen_done; } id.len = SC_PKCS15_MAX_ID_SIZE; rv = attr_find2(pPubTpl, ulPubCnt, pPrivTpl, ulPrivCnt, CKA_ID, &id.value, &id.len); if (rv == CKR_OK) keygen_args.prkey_args.id = pub_args.id = id; len = sizeof(priv_label) - 1; rv = attr_find(pPrivTpl, ulPrivCnt, CKA_LABEL, priv_label, &len); if (rv == CKR_OK) { priv_label[len] = '\0'; keygen_args.prkey_args.label = priv_label; } len = sizeof(pub_label) - 1; rv = attr_find(pPubTpl, ulPubCnt, CKA_LABEL, pub_label, &len); if (rv == CKR_OK) { pub_label[len] = '\0'; keygen_args.pubkey_label = pub_label; pub_args.label = pub_label; } rv = get_X509_usage_privk(pPrivTpl, ulPrivCnt, &keygen_args.prkey_args.x509_usage); if (rv == CKR_OK) rv = get_X509_usage_pubk(pPubTpl, ulPubCnt, &keygen_args.prkey_args.x509_usage); if (rv != CKR_OK) 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, (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); if (rc != 0) { sc_log(context, "sc_pkcs15_find_pubkey_by_id returned %d", rc); rv = sc_to_cryptoki_error(rc, "C_GenerateKeyPair"); goto kpgen_done; } } else { sc_log(context, "sc_pkcs15init_generate_key returned %d", rc); rv = sc_to_cryptoki_error(rc, "C_GenerateKeyPair"); goto kpgen_done; } /* 4. Create new pkcs11 public and private key object */ rc = __pkcs15_create_prkey_object(fw_data, priv_key_obj, &priv_any_obj); if (rc == 0) rc = __pkcs15_create_pubkey_object(fw_data, pub_key_obj, &pub_any_obj); if (rc != 0) { sc_log(context, "__pkcs15_create_pr/pubkey_object returned %d", rc); rv = sc_to_cryptoki_error(rc, "C_GenerateKeyPair"); goto kpgen_done; } pkcs15_add_object(slot, priv_any_obj, phPrivKey); pkcs15_add_object(slot, pub_any_obj, phPubKey); priv_prk_obj = (struct pkcs15_prkey_object *) priv_any_obj; priv_prk_obj->prv_pubkey = (struct pkcs15_pubkey_object *)pub_any_obj; /* Duplicate public key so that parameters can be retrieved even if public key object is deleted */ rv = sc_pkcs15_dup_pubkey(context, ((struct pkcs15_pubkey_object *)pub_any_obj)->pub_data, &priv_prk_obj->pub_data); kpgen_done: sc_pkcs15init_unbind(profile); sc_unlock(p11card->card); return rv; } #endif static CK_RV pkcs15_skey_destroy(struct sc_pkcs11_session *session, void *object) { #ifndef USE_PKCS15_INIT return CKR_FUNCTION_NOT_SUPPORTED; #else 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 refcount */ list_delete(&session->slot->objects, any_obj); /* Delete object in pkcs15 */ rv = __pkcs15_delete_object(fw_data, any_obj); sc_unlock(p11card->card); if (rv < 0) return sc_to_cryptoki_error(rv, "C_DestroyObject"); return CKR_OK; #endif } static CK_RV pkcs15_any_destroy(struct sc_pkcs11_session *session, void *object) { #ifndef USE_PKCS15_INIT return CKR_FUNCTION_NOT_SUPPORTED; #else struct pkcs15_data_object *obj = (struct pkcs15_data_object*) 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 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) return sc_to_cryptoki_error(rv, "C_DestroyObject"); /* Bind the profile */ rv = sc_pkcs15init_bind(p11card->card, "pkcs15", NULL, slot->app_info, &profile); if (rv < 0) { sc_unlock(p11card->card); return sc_to_cryptoki_error(rv, "C_DestroyObject"); } if(slot->app_info) aid = &slot->app_info->aid; rv = sc_pkcs15init_finalize_profile(p11card->card, profile, aid); if (rv) { sc_log(context, "Cannot finalize profile: %i", rv); return sc_to_cryptoki_error(rv, "C_DestroyObject"); } if (any_obj->related_pubkey) { struct pkcs15_any_object *ao_pubkey = (struct pkcs15_any_object *)any_obj->related_pubkey; struct pkcs15_pubkey_object *pubkey = any_obj->related_pubkey; /* Check if key is not removed in between */ if (list_locate(&session->slot->objects, ao_pubkey) > 0) { sc_log(context, "Found related pubkey %p", any_obj->related_pubkey); /* Delete reference to related certificate of the public key PKCS#11 object */ pubkey->pub_genfrom = NULL; if (ao_pubkey->p15_object == NULL) { sc_log(context, "Found related p15 object %p", ao_pubkey->p15_object); /* Unlink related public key FW object if it has no corresponding PKCS#15 object * and was created from certificate. */ --ao_pubkey->refcount; list_delete(&session->slot->objects, ao_pubkey); /* Delete public key object in pkcs15 */ if (pubkey->pub_data) { sc_log(context, "Found pub_data %p", pubkey->pub_data); sc_pkcs15_free_pubkey(pubkey->pub_data); pubkey->pub_data = NULL; } __pkcs15_delete_object(fw_data, ao_pubkey); } } } /* Delete object in smartcard (if corresponding PKCS#15 object exists) */ if (obj->base.p15_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 refcount */ list_delete(&session->slot->objects, any_obj); /* Delete object in pkcs15 */ rv = __pkcs15_delete_object(fw_data, any_obj); } sc_pkcs15init_unbind(profile); sc_unlock(p11card->card); if (rv < 0) return sc_to_cryptoki_error(rv, "C_DestroyObject"); return CKR_OK; #endif } static CK_RV pkcs15_get_random(struct sc_pkcs11_slot *slot, CK_BYTE_PTR p, CK_ULONG len) { struct sc_pkcs11_card *p11card = slot->p11card; 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"); } struct sc_pkcs11_framework_ops framework_pkcs15 = { pkcs15_bind, pkcs15_unbind, pkcs15_create_tokens, pkcs15_release_token, pkcs15_login, pkcs15_logout, pkcs15_change_pin, #ifdef USE_PKCS15_INIT pkcs15_initialize, pkcs15_init_pin, pkcs15_create_object, pkcs15_gen_keypair, #else NULL, NULL, NULL, NULL, #endif pkcs15_get_random }; static CK_RV pkcs15_set_attrib(struct sc_pkcs11_session *session, struct sc_pkcs15_object *p15_object, CK_ATTRIBUTE_PTR attr) { #ifndef USE_PKCS15_INIT return CKR_FUNCTION_NOT_SUPPORTED; #else struct sc_profile *profile = NULL; struct sc_pkcs11_slot *slot = session->slot; struct sc_pkcs11_card *p11card = session->slot->p11card; struct pkcs15_fw_data *fw_data = NULL; struct sc_aid *aid = NULL; struct sc_pkcs15_id id; 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) return sc_to_cryptoki_error(rv, "C_SetAttributeValue"); rv = sc_pkcs15init_bind(p11card->card, "pkcs15", NULL, slot->app_info, &profile); if (rv < 0) { sc_log(context, "C_SetAttributeValue: pkcs15init bind failed: %i", rv); sc_unlock(p11card->card); return sc_to_cryptoki_error(rv, "C_SetAttributeValue"); } if(slot->app_info) aid = &slot->app_info->aid; rv = sc_pkcs15init_finalize_profile(p11card->card, profile, aid); if (rv != CKR_OK) { sc_log(context, "C_SetAttributeValue: cannot finalize profile: %i", rv); sc_unlock(p11card->card); return sc_to_cryptoki_error(rv, "C_SetAttributeValue"); } switch(attr->type) { case CKA_LABEL: rv = sc_pkcs15init_change_attrib(fw_data->p15_card, profile, p15_object, P15_ATTR_TYPE_LABEL, attr->pValue, (unsigned int) attr->ulValueLen); break; case CKA_ID: if (attr->ulValueLen > SC_PKCS15_MAX_ID_SIZE) { rv = SC_ERROR_INVALID_ARGUMENTS; break; } memcpy(id.value, attr->pValue, attr->ulValueLen); id.len = attr->ulValueLen; rv = sc_pkcs15init_change_attrib(fw_data->p15_card, profile, p15_object, P15_ATTR_TYPE_ID, &id, sizeof(id)); break; 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; } ck_rv = sc_to_cryptoki_error(rv, "C_SetAttributeValue"); set_attr_done: sc_pkcs15init_unbind(profile); sc_unlock(p11card->card); return ck_rv; #endif } /* * PKCS#15 Certificate Object */ static void pkcs15_cert_release(void *obj) { struct pkcs15_cert_object *cert = (struct pkcs15_cert_object *) obj; struct sc_pkcs15_cert *cert_data = cert->cert_data; if (__pkcs15_release_object((struct pkcs15_any_object *) obj) == 0) if (cert_data) /* may never have been read */ sc_pkcs15_free_certificate(cert_data); } static CK_RV pkcs15_cert_set_attribute(struct sc_pkcs11_session *session, void *object, CK_ATTRIBUTE_PTR attr) { struct pkcs15_cert_object *cert = (struct pkcs15_cert_object*) object; return pkcs15_set_attrib(session, cert->base.p15_object, attr); } static CK_RV pkcs15_cert_get_attribute(struct sc_pkcs11_session *session, void *object, CK_ATTRIBUTE_PTR attr) { struct sc_pkcs11_card *p11card = NULL; struct pkcs15_cert_object *cert = (struct pkcs15_cert_object*) object; struct pkcs15_fw_data *fw_data = NULL; size_t len; 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: check_attribute_buffer(attr, sizeof(CK_OBJECT_CLASS)); *(CK_OBJECT_CLASS*)attr->pValue = CKO_CERTIFICATE; break; case CKA_TOKEN: check_attribute_buffer(attr, sizeof(CK_BBOOL)); *(CK_BBOOL*)attr->pValue = TRUE; break; case CKA_PRIVATE: check_attribute_buffer(attr, sizeof(CK_BBOOL)); *(CK_BBOOL*)attr->pValue = (cert->base.p15_object->flags & SC_PKCS15_CO_FLAG_PRIVATE) != 0; break; case CKA_MODIFIABLE: check_attribute_buffer(attr, sizeof(CK_BBOOL)); *(CK_BBOOL*)attr->pValue = FALSE; break; case CKA_LABEL: if (check_cert_data_read(fw_data, cert) != 0) { attr->ulValueLen = 0; return CKR_OK; } len = strnlen(cert->cert_p15obj->label, sizeof cert->cert_p15obj->label); check_attribute_buffer(attr, len); memcpy(attr->pValue, cert->cert_p15obj->label, len); break; case CKA_CERTIFICATE_TYPE: check_attribute_buffer(attr, sizeof(CK_CERTIFICATE_TYPE)); *(CK_CERTIFICATE_TYPE*)attr->pValue = CKC_X_509; break; case CKA_ID: #ifdef ZERO_CKAID_FOR_CA_CERTS if (cert->cert_info->authority) { check_attribute_buffer(attr, 1); *(unsigned char*)attr->pValue = 0; break; } #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)); *(CK_BBOOL*)attr->pValue = cert->cert_info->authority ? TRUE : FALSE; break; case CKA_VALUE: if (check_cert_data_read(fw_data, cert) != 0) { attr->ulValueLen = 0; return CKR_OK; } check_attribute_buffer(attr, cert->cert_data->data.len); memcpy(attr->pValue, cert->cert_data->data.value, cert->cert_data->data.len); break; case CKA_SERIAL_NUMBER: if (check_cert_data_read(fw_data, cert) != 0) { attr->ulValueLen = 0; return CKR_OK; } check_attribute_buffer(attr, cert->cert_data->serial_len); memcpy(attr->pValue, cert->cert_data->serial, cert->cert_data->serial_len); break; case CKA_SUBJECT: if (check_cert_data_read(fw_data, cert) != 0) { attr->ulValueLen = 0; return CKR_OK; } check_attribute_buffer(attr, cert->cert_data->subject_len); memcpy(attr->pValue, cert->cert_data->subject, cert->cert_data->subject_len); return CKR_OK; case CKA_ISSUER: if (check_cert_data_read(fw_data, cert) != 0) { attr->ulValueLen = 0; return CKR_OK; } check_attribute_buffer(attr, cert->cert_data->issuer_len); memcpy(attr->pValue, cert->cert_data->issuer, cert->cert_data->issuer_len); return CKR_OK; default: return CKR_ATTRIBUTE_TYPE_INVALID; } return CKR_OK; } #define ASN1_SET_TAG (SC_ASN1_SET | SC_ASN1_TAG_CONSTRUCTED) #define ASN1_SEQ_TAG (SC_ASN1_SEQUENCE | SC_ASN1_TAG_CONSTRUCTED) static CK_RV pkcs15_cert_cmp_attribute(struct sc_pkcs11_session *session, void *object, CK_ATTRIBUTE_PTR attr) { struct pkcs15_cert_object *cert = (struct pkcs15_cert_object*) object; struct sc_pkcs11_card *p11card = session->slot->p11card; struct pkcs15_fw_data *fw_data = NULL; const unsigned char *data = NULL, *_data = NULL; 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 * in the ASN.1 encoded SEQUENCE OF SET, * while OpenSC just keeps the SET in the issuer/subject field. */ case CKA_ISSUER: if (check_cert_data_read(fw_data, cert) != 0) break; if (cert->cert_data->issuer_len == 0) break; data = _data = (u8 *) attr->pValue; len = _len = attr->ulValueLen; if (cert->cert_data->issuer[0] == ASN1_SET_TAG && data[0] == ASN1_SEQ_TAG && len >= 2) data = sc_asn1_skip_tag(context, &_data, &_len, SC_ASN1_CONS | SC_ASN1_TAG_SEQUENCE, &len); if (len == cert->cert_data->issuer_len && !memcmp(cert->cert_data->issuer, data, len)) { sc_log(context, "pkcs15_cert_cmp_attribute() returns CKA_ISSUER matched"); return 1; } break; case CKA_SUBJECT: if (check_cert_data_read(fw_data, cert) != 0) break; if (cert->cert_data->subject_len == 0) break; data = _data = (u8 *) attr->pValue; len = _len = attr->ulValueLen; if (cert->cert_data->subject[0] == ASN1_SET_TAG && data[0] == ASN1_SEQ_TAG && len >= 2) data = sc_asn1_skip_tag(context, &_data, &_len, SC_ASN1_CONS | SC_ASN1_TAG_SEQUENCE, &len); if (len == cert->cert_data->subject_len && !memcmp(cert->cert_data->subject, data, len)) { sc_log(context, "pkcs15_cert_cmp_attribute() returns CKA_SUBJECT matched"); return 1; } break; default: return sc_pkcs11_any_cmp_attribute(session, object, attr); } sc_log(context, "pkcs15_cert_cmp_attribute() returns not matched"); return 0; } struct sc_pkcs11_object_ops pkcs15_cert_ops = { pkcs15_cert_release, pkcs15_cert_set_attribute, pkcs15_cert_get_attribute, pkcs15_cert_cmp_attribute, pkcs15_any_destroy, NULL, /* get_size */ NULL, /* sign */ NULL, /* unwrap_key */ NULL, /* decrypt */ NULL, /* encrypt */ NULL, /* derive */ NULL, /* can_do */ NULL, /* init_params */ NULL /* wrap_key */ }; /* * PKCS#15 Private Key Object */ static void pkcs15_prkey_release(void *object) { struct pkcs15_prkey_object *prkey = (struct pkcs15_prkey_object*) object; struct sc_pkcs15_pubkey *key_data = prkey->pub_data; if (__pkcs15_release_object((struct pkcs15_any_object *) object) == 0) if (key_data) sc_pkcs15_free_pubkey(key_data); } static CK_RV pkcs15_prkey_set_attribute(struct sc_pkcs11_session *session, void *object, CK_ATTRIBUTE_PTR attr) { struct pkcs15_prkey_object *prkey = (struct pkcs15_prkey_object*) object; return pkcs15_set_attrib(session, prkey->base.p15_object, attr); } static CK_RV pkcs15_prkey_get_attribute(struct sc_pkcs11_session *session, void *object, CK_ATTRIBUTE_PTR attr) { struct pkcs15_prkey_object *prkey = (struct pkcs15_prkey_object*) object; struct sc_pkcs11_card *p11card = NULL; struct pkcs15_fw_data *fw_data = NULL; struct sc_pkcs15_pubkey *key = NULL; unsigned int usage; size_t len; 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 * (the key is supposed to be safely locked away after all). * * To work around this, we hope that we either have an associated * public key, or we try to find a certificate with the * corresponding public key. * * Note: We do the same thing for CKA_PUBLIC_EXPONENT as some * applications assume they can get that from the private * key, something PKCS#11 doesn't guarantee. */ 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_EC_PARAMS)) { /* First see if we have an associated public key */ if (prkey->pub_data) { key = prkey->pub_data; } else { /* Try to find public key or certificate with the public key */ unsigned int i; for (i = 0; i < fw_data->num_objects; i++) { struct pkcs15_any_object *obj = fw_data->objects[i]; struct pkcs15_cert_object *cert; if (is_cert(obj)) { cert = (struct pkcs15_cert_object*) obj; if (cert->cert_prvkey != prkey) continue; if (check_cert_data_read(fw_data, cert) == 0) { key = cert->cert_pubkey->pub_data; sc_log(context, "found friend certificate's public key %p", key); } } else if (is_pubkey(obj)) { struct pkcs15_pubkey_object *pubkey = (struct pkcs15_pubkey_object *) obj; if (!pubkey->pub_data) continue; if (sc_pkcs15_compare_id(&pubkey->pub_info->id, &prkey->prv_info->id)) { prkey->prv_pubkey = pubkey; key = pubkey->pub_data; sc_log(context, "found friend public key %p", key); } } } } } switch (attr->type) { case CKA_CLASS: check_attribute_buffer(attr, sizeof(CK_OBJECT_CLASS)); *(CK_OBJECT_CLASS*)attr->pValue = CKO_PRIVATE_KEY; break; case CKA_TOKEN: check_attribute_buffer(attr, sizeof(CK_BBOOL)); *(CK_BBOOL*)attr->pValue = TRUE; break; case CKA_ALWAYS_SENSITIVE: check_attribute_buffer(attr, sizeof(CK_BBOOL)); *(CK_BBOOL*)attr->pValue = (prkey->prv_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 = (prkey->prv_info->access_flags & SC_PKCS15_PRKEY_ACCESS_NEVEREXTRACTABLE) != 0; break; case CKA_SENSITIVE: check_attribute_buffer(attr, sizeof(CK_BBOOL)); *(CK_BBOOL*)attr->pValue = (prkey->prv_info->access_flags & SC_PKCS15_PRKEY_ACCESS_SENSITIVE) != 0; break; case CKA_LOCAL: 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: check_attribute_buffer(attr, sizeof(CK_BBOOL)); 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 = (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); check_attribute_buffer(attr, len); memcpy(attr->pValue, prkey->prv_p15obj->label, len); break; case CKA_KEY_TYPE: check_attribute_buffer(attr, sizeof(CK_KEY_TYPE)); switch (prkey->prv_p15obj->type) { case SC_PKCS15_TYPE_PRKEY_RSA: *(CK_KEY_TYPE*)attr->pValue = CKK_RSA; break; 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; default: return CKR_GENERAL_ERROR; /* Internal error*/ } break; case CKA_ID: check_attribute_buffer(attr, prkey->prv_info->id.len); memcpy(attr->pValue, prkey->prv_info->id.value, prkey->prv_info->id.len); break; case CKA_KEY_GEN_MECHANISM: check_attribute_buffer(attr, sizeof(CK_MECHANISM_TYPE)); *(CK_MECHANISM_TYPE*)attr->pValue = CK_UNAVAILABLE_INFORMATION; break; case CKA_DECRYPT: case CKA_SIGN: case CKA_SIGN_RECOVER: case CKA_UNWRAP: case CKA_DERIVE: case CKA_OPENSC_NON_REPUDIATION: /* TODO seems to be obsolete */ /* Combine the usage bits of all split keys */ for (usage = 0; prkey; prkey = prkey->prv_next) usage |= prkey->prv_info->usage; return get_usage_bit(usage, attr); case CKA_MODULUS: return get_modulus(key, attr); 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) *(CK_ULONG *) attr->pValue = key->u.ec.params.field_length; else *(CK_ULONG *) attr->pValue = (key->u.ec.ecpointQ.len - 1) / 2 *8; } return CKR_OK; default: *(CK_ULONG *) attr->pValue = prkey->prv_info->modulus_length; return CKR_OK; } case CKA_PUBLIC_EXPONENT: return get_public_exponent(key, attr); case CKA_PRIVATE_EXPONENT: case CKA_PRIME_1: case CKA_PRIME_2: case CKA_EXPONENT_1: case CKA_EXPONENT_2: case CKA_COEFFICIENT: return CKR_ATTRIBUTE_SENSITIVE; case CKA_SUBJECT: case CKA_START_DATE: case CKA_END_DATE: attr->ulValueLen = 0; return CKR_OK; case CKA_GOSTR3410_PARAMS: if (prkey->prv_info && prkey->prv_info->params.len) return get_gostr3410_params(prkey->prv_info->params.data, prkey->prv_info->params.len, attr); else return CKR_ATTRIBUTE_TYPE_INVALID; case CKA_EC_PARAMS: return get_ec_pubkey_params(key, attr); /* get from pubkey for now */ default: return CKR_ATTRIBUTE_TYPE_INVALID; } return CKR_OK; } 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, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulDataLen) { 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; 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); 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)) prkey = prkey->prv_next; if (prkey == NULL) return CKR_KEY_FUNCTION_NOT_PERMITTED; switch (pMechanism->mechanism) { case CKM_RSA_PKCS: 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_TYPE_01 | SC_ALGORITHM_RSA_HASH_MD5; break; case CKM_SHA1_RSA_PKCS: 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_TYPE_01 | SC_ALGORITHM_RSA_HASH_SHA256; break; case CKM_SHA384_RSA_PKCS: 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_TYPE_01 | SC_ALGORITHM_RSA_HASH_SHA512; break; case CKM_RIPEMD160_RSA_PKCS: 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; break; 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; } 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); 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.", rc); if (rc > 0) { *pulDataLen = rc; return CKR_OK; } 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, 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_prkey_object *prkey; unsigned char decrypted[512]; /* FIXME: Will not work for keys above 4096 bits */ 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; while (prkey && !(prkey->prv_info->usage & (SC_PKCS15_PRKEY_USAGE_DECRYPT|SC_PKCS15_PRKEY_USAGE_UNWRAP))) prkey = prkey->prv_next; if (prkey == NULL) return CKR_KEY_FUNCTION_NOT_PERMITTED; /* 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; 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; } rv = sc_lock(p11card->card); if (rv < 0) 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), pMechanism); sc_unlock(p11card->card); sc_log(context, "Decryption complete."); /* Handle following code in constant-time * to prevent Marvin attack for PKCS#1 v1.5 padding. */ /* only padding error must be handled in constant-time way, * other error can be returned straight away */ if ((~constant_time_eq_i(rv, SC_ERROR_WRONG_PADDING) & constant_time_lt_s(sizeof(decrypted), (size_t)rv))) return sc_to_cryptoki_error(rv, "C_Decrypt"); /* 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; } static CK_RV pkcs15_prkey_derive(struct sc_pkcs11_session *session, void *obj, CK_MECHANISM_PTR pMechanism, CK_BYTE_PTR pParameters, CK_ULONG ulParametersLen, 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_prkey_object *prkey = (struct pkcs15_prkey_object *) obj; 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"); 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)) prkey = prkey->prv_next; if (prkey == NULL) return CKR_KEY_FUNCTION_NOT_PERMITTED; 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"); } /* TODO DEE This may not be the place to get the parameters, * But its the last PKCS11 aware routine. * RSA parameters would be null. */ switch (prkey->base.p15_object->type) { 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; 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, &len); *pulDataLen = len; /* this may have been a request for size */ if (need_unlock) sc_unlock(p11card->card); sc_log(context, "Derivation complete. Result %d.", rv); if (rv < 0) return sc_to_cryptoki_error(rv, "C_DeriveKey"); return CKR_OK; } static CK_RV pkcs15_prkey_can_do(struct sc_pkcs11_session *session, void *obj, CK_MECHANISM_TYPE mech_type, unsigned int flags) { 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 sc_pkcs15_prkey_info *pkinfo = NULL; 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) LOG_FUNC_RETURN(context, CKR_KEY_FUNCTION_NOT_PERMITTED); pkinfo = prkey->prv_info; /* Return if there are no usage algorithms specified for this key. */ if (!pkinfo->algo_refs[0]) 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++) { /* Look for algorithm supported by token referenced in the list of key's algorithms */ for (jj=0;jjreference; jj++) if (pkinfo->algo_refs[ii] == (token_algos + jj)->reference) break; if ((jj == SC_MAX_SUPPORTED_ALGORITHMS) || !(token_algos + jj)->reference) LOG_FUNC_RETURN(context, CKR_GENERAL_ERROR); if ((token_algos + jj)->mechanism != mech_type) continue; if (flags == CKF_SIGN) if ((token_algos + jj)->operations & SC_PKCS15_ALGO_OP_COMPUTE_SIGNATURE) break; if (flags == CKF_DECRYPT) if ((token_algos + jj)->operations & SC_PKCS15_ALGO_OP_DECIPHER) break; } if (ii == SC_MAX_SUPPORTED_ALGORITHMS || !pkinfo->algo_refs[ii]) 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; } struct sc_pkcs11_object_ops pkcs15_prkey_ops = { pkcs15_prkey_release, pkcs15_prkey_set_attribute, pkcs15_prkey_get_attribute, sc_pkcs11_any_cmp_attribute, pkcs15_any_destroy, NULL, /* get_size */ pkcs15_prkey_sign, pkcs15_prkey_unwrap, pkcs15_prkey_decrypt, NULL, /* encrypt */ pkcs15_prkey_derive, pkcs15_prkey_can_do, pkcs15_prkey_init_params, NULL /* wrap_key */ }; /* * PKCS#15 RSA Public Key Object */ static void pkcs15_pubkey_release(void *object) { struct pkcs15_pubkey_object *pubkey = (struct pkcs15_pubkey_object*) object; struct sc_pkcs15_pubkey *key_data = pubkey->pub_data; if (__pkcs15_release_object((struct pkcs15_any_object *) object) == 0) if (key_data) sc_pkcs15_free_pubkey(key_data); } static CK_RV pkcs15_pubkey_set_attribute(struct sc_pkcs11_session *session, void *object, CK_ATTRIBUTE_PTR attr) { struct pkcs15_pubkey_object *pubkey = (struct pkcs15_pubkey_object*) object; return pkcs15_set_attrib(session, pubkey->base.p15_object, attr); } static CK_RV pkcs15_pubkey_get_attribute(struct sc_pkcs11_session *session, void *object, CK_ATTRIBUTE_PTR attr) { struct sc_pkcs11_card *p11card = NULL; struct pkcs15_pubkey_object *pubkey = (struct pkcs15_pubkey_object*) object; struct pkcs15_cert_object *cert = NULL; struct pkcs15_fw_data *fw_data = NULL; size_t len; sc_log(context, "pkcs15_pubkey_get_attribute() called"); 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_EXPONENT: case CKA_EC_PARAMS: case CKA_EC_POINT: if (pubkey->pub_data == NULL) if (SC_SUCCESS != check_cert_data_read(fw_data, cert)) return sc_to_cryptoki_error(SC_ERROR_INTERNAL, "check_cert_data_read"); break; } switch (attr->type) { case CKA_CLASS: check_attribute_buffer(attr, sizeof(CK_OBJECT_CLASS)); *(CK_OBJECT_CLASS*)attr->pValue = CKO_PUBLIC_KEY; break; case CKA_TOKEN: check_attribute_buffer(attr, sizeof(CK_BBOOL)); *(CK_BBOOL*)attr->pValue = TRUE; break; case CKA_SENSITIVE: /* By PKCS#11 v2.20 public key cannot have SENSITIVE attr TRUE */ check_attribute_buffer(attr, sizeof(CK_BBOOL)); *(CK_BBOOL*)attr->pValue = FALSE; break; 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; else /* no pub_info structure, falling back to TRUE */ *(CK_BBOOL*)attr->pValue = TRUE; break; case CKA_PRIVATE: check_attribute_buffer(attr, sizeof(CK_BBOOL)); if (pubkey->pub_p15obj) *(CK_BBOOL*)attr->pValue = (pubkey->pub_p15obj->flags & SC_PKCS15_CO_FLAG_PRIVATE) != 0; else if (cert && cert->cert_p15obj) *(CK_BBOOL*)attr->pValue = (cert->pub_p15obj->flags & SC_PKCS15_CO_FLAG_PRIVATE) != 0; else 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)); 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) { len = strnlen(pubkey->pub_p15obj->label, sizeof pubkey->pub_p15obj->label); check_attribute_buffer(attr, len); memcpy(attr->pValue, pubkey->pub_p15obj->label, len); } else if (cert && cert->cert_p15obj) { len = strnlen(cert->cert_p15obj->label, sizeof cert->cert_p15obj->label); check_attribute_buffer(attr, len); memcpy(attr->pValue, cert->cert_p15obj->label, len); } else { return CKR_ATTRIBUTE_TYPE_INVALID; } break; case CKA_KEY_TYPE: check_attribute_buffer(attr, sizeof(CK_KEY_TYPE)); /* TODO: -DEE why would we not have a pubkey->pub_data? */ /* 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 *(CK_KEY_TYPE*)attr->pValue = CKK_RSA; break; case CKA_ID: if (pubkey->pub_info) { check_attribute_buffer(attr, pubkey->pub_info->id.len); memcpy(attr->pValue, pubkey->pub_info->id.value, pubkey->pub_info->id.len); } else if (cert && cert->cert_info) { check_attribute_buffer(attr, cert->cert_info->id.len); memcpy(attr->pValue, cert->cert_info->id.value, cert->cert_info->id.len); } else { return CKR_ATTRIBUTE_TYPE_INVALID; } break; case CKA_KEY_GEN_MECHANISM: check_attribute_buffer(attr, sizeof(CK_MECHANISM_TYPE)); *(CK_MECHANISM_TYPE*)attr->pValue = CK_UNAVAILABLE_INFORMATION; break; case CKA_ENCRYPT: case CKA_WRAP: case CKA_VERIFY: case CKA_VERIFY_RECOVER: case CKA_DERIVE: if (pubkey->pub_info) return get_usage_bit(pubkey->pub_info->usage, attr); else return get_usage_bit(SC_PKCS15_PRKEY_USAGE_ENCRYPT |SC_PKCS15_PRKEY_USAGE_VERIFY | SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER, attr); case CKA_MODULUS: return get_modulus(pubkey->pub_data, attr); case CKA_MODULUS_BITS: 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 * can be converted into a EVP_KEY with d2i_PUBKEY * CKA_SPKI is defined internally as a CKA_VENDOR_DFINED attribute. */ case CKA_VALUE: case CKA_SPKI: if (attr->type != CKA_SPKI && 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) { 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) { unsigned char *value = NULL; size_t len; if (attr->type != CKA_SPKI) { 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)) return sc_to_cryptoki_error(SC_ERROR_INTERNAL, "C_GetAttributeValue"); } if (attr->pValue == NULL_PTR) { attr->ulValueLen = len; free(value); return CKR_OK; } if (attr->ulValueLen < len) { attr->ulValueLen = len; free(value); return CKR_BUFFER_TOO_SMALL; } attr->ulValueLen = len; 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) { 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) { check_attribute_buffer(attr, cert->cert_data->data.len); memcpy(attr->pValue, cert->cert_data->data.value, cert->cert_data->data.len); } else { return CKR_ATTRIBUTE_TYPE_INVALID; } break; case CKA_GOSTR3410_PARAMS: if (pubkey->pub_info && pubkey->pub_info->params.len) return get_gostr3410_params(pubkey->pub_info->params.data, pubkey->pub_info->params.len, attr); else return CKR_ATTRIBUTE_TYPE_INVALID; case CKA_EC_PARAMS: return get_ec_pubkey_params(pubkey->pub_data, attr); case CKA_EC_POINT: return get_ec_pubkey_point(pubkey->pub_data, attr); default: return CKR_ATTRIBUTE_TYPE_INVALID; } return CKR_OK; } struct sc_pkcs11_object_ops pkcs15_pubkey_ops = { pkcs15_pubkey_release, pkcs15_pubkey_set_attribute, pkcs15_pubkey_get_attribute, sc_pkcs11_any_cmp_attribute, pkcs15_any_destroy, NULL, /* get_size */ NULL, /* sign */ NULL, /* unwrap_key */ NULL, /* decrypt */ NULL, /* ecrypt */ NULL, /* derive */ NULL, /* can_do */ NULL, /* init_params */ NULL /* wrap_key */ }; /* PKCS#15 Data Object*/ static void pkcs15_dobj_release(void *object) { __pkcs15_release_object((struct pkcs15_any_object *) object); } static CK_RV pkcs15_dobj_set_attribute(struct sc_pkcs11_session *session, void *object, CK_ATTRIBUTE_PTR attr) { struct pkcs15_data_object *dobj = (struct pkcs15_data_object*) object; return pkcs15_set_attrib(session, dobj->base.p15_object, attr); } 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; 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"); 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) return sc_to_cryptoki_error(rv, "C_GetAttributeValue"); return rv; } static CK_RV data_value_to_attr(CK_ATTRIBUTE_PTR attr, struct sc_pkcs15_data *data) { if (!attr || !data) return CKR_ATTRIBUTE_VALUE_INVALID; check_attribute_buffer(attr, data->data_len); memcpy(attr->pValue, data->data, data->data_len); return CKR_OK; } static CK_RV pkcs15_dobj_get_attribute(struct sc_pkcs11_session *session, void *object, CK_ATTRIBUTE_PTR attr) { struct pkcs15_data_object *dobj = (struct pkcs15_data_object*) object; struct sc_pkcs15_data *data = NULL; CK_RV rv; size_t len; int r; unsigned char *buf = NULL; sc_log(context, "pkcs15_dobj_get_attribute() called"); switch (attr->type) { case CKA_CLASS: check_attribute_buffer(attr, sizeof(CK_OBJECT_CLASS)); *(CK_OBJECT_CLASS*)attr->pValue = CKO_DATA; break; case CKA_TOKEN: check_attribute_buffer(attr, sizeof(CK_BBOOL)); *(CK_BBOOL*)attr->pValue = TRUE; break; case CKA_PRIVATE: check_attribute_buffer(attr, sizeof(CK_BBOOL)); *(CK_BBOOL*)attr->pValue = (dobj->base.p15_object->flags & SC_PKCS15_CO_FLAG_PRIVATE) != 0; break; case CKA_MODIFIABLE: check_attribute_buffer(attr, sizeof(CK_BBOOL)); *(CK_BBOOL*)attr->pValue = (dobj->base.p15_object->flags & 0x02) != 0; break; case CKA_LABEL: len = strnlen(dobj->base.p15_object->label, sizeof dobj->base.p15_object->label); check_attribute_buffer(attr, len); memcpy(attr->pValue, dobj->base.p15_object->label, len); break; case CKA_APPLICATION: len = strlen(dobj->info->app_label); check_attribute_buffer(attr, len); memcpy(attr->pValue, dobj->info->app_label, len); break; #if 0 case CKA_ID: check_attribute_buffer(attr, dobj->info->id.len); memcpy(attr->pValue, dobj->info->id.value, dobj->info->id.len); break; #endif case CKA_OBJECT_ID: if (!sc_valid_oid(&dobj->info->app_oid)) { attr->ulValueLen = -1; return CKR_ATTRIBUTE_TYPE_INVALID; } r = sc_asn1_encode_object_id(NULL, &len, &dobj->info->app_oid); if (r) { sc_log(context, "data_get_attr(): encode OID error %i", r); return CKR_FUNCTION_FAILED; } check_attribute_buffer(attr, len); r = sc_asn1_encode_object_id(&buf, &len, &dobj->info->app_oid); if (r) { sc_log(context, "data_get_attr(): encode OID error %i", r); return CKR_FUNCTION_FAILED; } memcpy(attr->pValue, buf, len); 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) { if (data) { rv = data_value_to_attr(attr, data); } else { attr->ulValueLen = 0; attr->pValue = NULL_PTR; } } if (data) { free(data->data); free(data); } if (rv != CKR_OK) return rv; break; default: return CKR_ATTRIBUTE_TYPE_INVALID; } return CKR_OK; } struct sc_pkcs11_object_ops pkcs15_dobj_ops = { pkcs15_dobj_release, pkcs15_dobj_set_attribute, pkcs15_dobj_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, /* 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, /* init_params */ NULL /* wrap_key */ }; /* PKCS#15 Secret Key Objects */ /* TODO Currently only session objects */ static void pkcs15_skey_release(void *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); } } } static CK_RV pkcs15_skey_set_attribute(struct sc_pkcs11_session *session, void *object, CK_ATTRIBUTE_PTR attr) { struct pkcs15_skey_object *skey = (struct pkcs15_skey_object*) object; /* TODO DEE Assume a session based token, and only * change in memory, and only selected types * The pkcs15_set_attrib assumes the object is on the card.... * When skey support on the card is added this needs to be changed */ 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; memcpy(skey->info->data.value, attr->pValue, attr->ulValueLen); skey->info->data.len = attr->ulValueLen; } break; default: return pkcs15_set_attrib(session, skey->base.p15_object, attr); } return CKR_OK; } static CK_RV pkcs15_skey_get_attribute(struct sc_pkcs11_session *session, void *object, CK_ATTRIBUTE_PTR attr) { struct pkcs15_skey_object *skey = (struct pkcs15_skey_object*) object; size_t len; sc_log(context, "pkcs15_skey_get_attribute() called"); switch (attr->type) { case CKA_CLASS: check_attribute_buffer(attr, sizeof(CK_OBJECT_CLASS)); *(CK_OBJECT_CLASS*)attr->pValue = CKO_SECRET_KEY; break; case CKA_TOKEN: check_attribute_buffer(attr, sizeof(CK_BBOOL)); *(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_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_TRUE : CK_FALSE; /*TODO Why no definition of the flag */ break; case CKA_LABEL: len = strnlen(skey->base.p15_object->label, sizeof skey->base.p15_object->label); check_attribute_buffer(attr, len); memcpy(attr->pValue, skey->base.p15_object->label, len); break; case CKA_KEY_TYPE: check_attribute_buffer(attr, sizeof(CK_KEY_TYPE)); if (skey->info) *(CK_OBJECT_CLASS*)attr->pValue = skey->info->key_type; break; case CKA_ENCRYPT: case CKA_DECRYPT: case CKA_SIGN: case CKA_SIGN_RECOVER: case CKA_WRAP: case CKA_UNWRAP: case CKA_VERIFY: case CKA_VERIFY_RECOVER: case CKA_DERIVE: if (skey->info) return get_usage_bit(skey->info->usage, attr); else return get_usage_bit(SC_PKCS15_PRKEY_USAGE_ENCRYPT |SC_PKCS15_PRKEY_USAGE_DECRYPT |SC_PKCS15_PRKEY_USAGE_WRAP |SC_PKCS15_PRKEY_USAGE_UNWRAP, attr); break; case CKA_ID: 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)); 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); memcpy(attr->pValue, skey->info->data.value, skey->info->data.len); break; default: return CKR_ATTRIBUTE_TYPE_INVALID; } 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 */ struct sc_pkcs11_object_ops pkcs15_skey_ops = { pkcs15_skey_release, pkcs15_skey_set_attribute, pkcs15_skey_get_attribute, sc_pkcs11_any_cmp_attribute, pkcs15_skey_destroy, NULL, /* get_size */ NULL, /* sign */ pkcs15_skey_unwrap, pkcs15_skey_decrypt, /* decrypt */ pkcs15_skey_encrypt, /* encrypt */ NULL, /* derive */ NULL, /* can_do */ NULL, /* init_params */ pkcs15_skey_wrap /* wrap_key */ }; /* * get_attribute helpers */ static CK_RV get_bignum(sc_pkcs15_bignum_t *bn, CK_ATTRIBUTE_PTR attr) { check_attribute_buffer(attr, bn->len); memcpy(attr->pValue, bn->data, bn->len); return CKR_OK; } static CK_RV get_bignum_bits(sc_pkcs15_bignum_t *bn, CK_ATTRIBUTE_PTR attr) { CK_ULONG bits, mask; if (!bn || !bn->len || !bn->data) return CKR_DEVICE_ERROR; bits = bn->len * 8; for (mask = 0x80; mask; mask >>= 1, bits--) if (bn->data[0] & mask) break; check_attribute_buffer(attr, sizeof(bits)); *(CK_ULONG *) attr->pValue = bits; return CKR_OK; } static CK_RV get_modulus(struct sc_pkcs15_pubkey *key, CK_ATTRIBUTE_PTR attr) { if (key == NULL) return CKR_ATTRIBUTE_TYPE_INVALID; switch (key->algorithm) { case SC_ALGORITHM_RSA: return get_bignum(&key->u.rsa.modulus, attr); } return CKR_ATTRIBUTE_TYPE_INVALID; } static CK_RV get_modulus_bits(struct sc_pkcs15_pubkey *key, CK_ATTRIBUTE_PTR attr) { if (key == NULL) return CKR_ATTRIBUTE_TYPE_INVALID; switch (key->algorithm) { case SC_ALGORITHM_RSA: return get_bignum_bits(&key->u.rsa.modulus, attr); } return CKR_ATTRIBUTE_TYPE_INVALID; } static CK_RV get_public_exponent(struct sc_pkcs15_pubkey *key, CK_ATTRIBUTE_PTR attr) { if (key == NULL) return CKR_ATTRIBUTE_TYPE_INVALID; switch (key->algorithm) { case SC_ALGORITHM_RSA: return get_bignum(&key->u.rsa.exponent, attr); } return CKR_ATTRIBUTE_TYPE_INVALID; } 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; if (key->alg_id == NULL) 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 */ if (key->u.ec.params.der.value) { check_attribute_buffer(attr,key->u.ec.params.der.len); memcpy(attr->pValue, key->u.ec.params.der.value, key->u.ec.params.der.len); return CKR_OK; } ecp = (struct sc_ec_parameters *) key->alg_id->params; if (ecp && ecp->der.value && ecp->der.len) { check_attribute_buffer(attr, ecp->der.len); memcpy(attr->pValue, ecp->der.value, ecp->der.len); return CKR_OK; } } return CKR_ATTRIBUTE_TYPE_INVALID; } static CK_RV get_ec_pubkey_point(struct sc_pkcs15_pubkey *key, CK_ATTRIBUTE_PTR attr) { unsigned char *value = NULL; size_t value_len = 0; int rc; if (key == NULL) return CKR_ATTRIBUTE_TYPE_INVALID; switch (key->algorithm) { case SC_ALGORITHM_EDDSA: case SC_ALGORITHM_XEDDSA: rc = sc_pkcs15_encode_pubkey_eddsa(context, &key->u.eddsa, &value, &value_len); if (rc != SC_SUCCESS) return sc_to_cryptoki_error(rc, NULL); 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; 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); 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; } return CKR_ATTRIBUTE_TYPE_INVALID; } static CK_RV get_gostr3410_params(const u8 *params, size_t params_len, CK_ATTRIBUTE_PTR attr) { size_t i; if (!params || params_len == sizeof(int)) return CKR_ATTRIBUTE_TYPE_INVALID; for (i = 0; i < sizeof(gostr3410_param_oid)/sizeof(gostr3410_param_oid[0]); ++i) { if (gostr3410_param_oid[i].oid_id == ((int*)params)[0]) { check_attribute_buffer(attr, gostr3410_param_oid[i].encoded_oid_size); memcpy(attr->pValue, gostr3410_param_oid[i].encoded_oid, gostr3410_param_oid[i].encoded_oid_size); return CKR_OK; } } return CKR_ATTRIBUTE_TYPE_INVALID; } /* * Map pkcs15 usage bits to pkcs11 usage attributes. * * It's not totally clear to me whether SC_PKCS15_PRKEY_USAGE_NONREPUDIATION should * be treated as being equivalent with CKA_SIGN or not... */ static CK_RV get_usage_bit(unsigned int usage, CK_ATTRIBUTE_PTR attr) { static struct { CK_ATTRIBUTE_TYPE type; unsigned int flag; } flag_mapping[] = { { CKA_ENCRYPT, SC_PKCS15_PRKEY_USAGE_ENCRYPT }, { CKA_DECRYPT, SC_PKCS15_PRKEY_USAGE_DECRYPT }, { CKA_SIGN, SC_PKCS15_PRKEY_USAGE_SIGN|SC_PKCS15_PRKEY_USAGE_NONREPUDIATION }, { CKA_SIGN_RECOVER, SC_PKCS15_PRKEY_USAGE_SIGNRECOVER }, { CKA_WRAP, SC_PKCS15_PRKEY_USAGE_WRAP }, { CKA_UNWRAP, SC_PKCS15_PRKEY_USAGE_UNWRAP }, { CKA_VERIFY, SC_PKCS15_PRKEY_USAGE_VERIFY }, { CKA_VERIFY_RECOVER, SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER }, { CKA_DERIVE, SC_PKCS15_PRKEY_USAGE_DERIVE }, { CKA_OPENSC_NON_REPUDIATION, SC_PKCS15_PRKEY_USAGE_NONREPUDIATION }, { 0, 0 } }; unsigned int mask = 0, j; for (j = 0; (mask = flag_mapping[j].flag) != 0; j++) { if (flag_mapping[j].type == attr->type) break; } if (mask == 0) return CKR_ATTRIBUTE_TYPE_INVALID; check_attribute_buffer(attr, sizeof(CK_BBOOL)); *(CK_BBOOL*)attr->pValue = (usage & mask)? TRUE : FALSE; return CKR_OK; } static CK_RV register_gost_mechanisms(struct sc_pkcs11_card *p11card, int flags) { CK_MECHANISM_INFO mech_info; sc_pkcs11_mechanism_type_t *mt; CK_RV rc; mech_info.flags = CKF_HW | CKF_SIGN | CKF_DECRYPT; #ifdef ENABLE_OPENSSL /* That practise definitely conflicts with CKF_HW -- andre 2010-11-28 */ mech_info.flags |= CKF_VERIFY; #endif mech_info.ulMinKeySize = SC_PKCS15_GOSTR3410_KEYSIZE; mech_info.ulMaxKeySize = SC_PKCS15_GOSTR3410_KEYSIZE; if (flags & SC_ALGORITHM_GOSTR3410_HASH_NONE) { mt = sc_pkcs11_new_fw_mechanism(CKM_GOSTR3410, &mech_info, CKK_GOSTR3410, 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_GOSTR3410_HASH_GOSTR3411) { mt = sc_pkcs11_new_fw_mechanism(CKM_GOSTR3410_WITH_GOSTR3411, &mech_info, CKK_GOSTR3410, 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_GOSTR3410_KEY_PAIR_GEN, &mech_info, CKK_GOSTR3410, 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_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 = NULL, *registered_mt = NULL; CK_FLAGS ec_flags = 0; CK_RV rc; if (ext_flags & SC_ALGORITHM_EXT_EC_F_P) ec_flags |= CKF_EC_F_P; if (ext_flags & SC_ALGORITHM_EXT_EC_F_2M) ec_flags |= CKF_EC_F_2M; if (ext_flags & SC_ALGORITHM_EXT_EC_ECPARAMETERS) ec_flags |= CKF_EC_ECPARAMETERS; if (ext_flags & SC_ALGORITHM_EXT_EC_NAMEDCURVE) ec_flags |= CKF_EC_NAMEDCURVE; if (ext_flags & SC_ALGORITHM_EXT_EC_UNCOMPRESES) ec_flags |= CKF_EC_UNCOMPRESS; if (ext_flags & SC_ALGORITHM_EXT_EC_COMPRESS) ec_flags |= CKF_EC_COMPRESS; 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; /* 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; 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; } 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, 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; } /* 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 | CKF_VERIFY); mech_info.flags |= CKF_DERIVE; 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, 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, 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; mech_info.flags |= ec_flags; 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, 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; } /* * Mechanism handling * FIXME: We should consult the card's algorithm list to * find out what operations it supports */ static CK_RV 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, aes_min_key_size, aes_max_key_size; unsigned long ec_ext_flags; sc_pkcs11_mechanism_type_t *mt, *registered_mt; unsigned int num; 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); mech_info.flags = CKF_HW | CKF_SIGN | CKF_DECRYPT; #ifdef ENABLE_OPENSSL /* 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 * flags, based on the assumption that cards don't * support different modes for different key *sizes*. */ num = card->algorithm_count; alg_info = card->algorithms; while (num--) { switch (alg_info->algorithm) { case SC_ALGORITHM_RSA: if (alg_info->key_length < mech_info.ulMinKeySize) mech_info.ulMinKeySize = alg_info->key_length; if (alg_info->key_length > mech_info.ulMaxKeySize) mech_info.ulMaxKeySize = alg_info->key_length; rsa_flags |= alg_info->flags; break; case SC_ALGORITHM_EC: if (alg_info->key_length < ec_min_key_size) ec_min_key_size = alg_info->key_length; if (alg_info->key_length > ec_max_key_size) 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++; } /* * 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)) { if (gostr_flags & SC_ALGORITHM_GOSTR3410_RAW) gostr_flags |= SC_ALGORITHM_GOSTR3410_HASH_NONE; rc = register_gost_mechanisms(p11card, gostr_flags); if (rc != CKR_OK) return rc; } /* 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, 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 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, 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, 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, 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, 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, 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_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_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; } 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_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, 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; } static int lock_card(struct pkcs15_fw_data *fw_data) { int rc; if ((rc = sc_lock(fw_data->p15_card->card)) < 0) sc_log(context, "Failed to lock card (%d)", rc); else fw_data->locked++; return rc; } static int unlock_card(struct pkcs15_fw_data *fw_data) { while (fw_data->locked) { sc_unlock(fw_data->p15_card->card); fw_data->locked--; } return 0; } OpenSC-0.26.1/src/pkcs11/framework-pkcs15init.c000066400000000000000000000132441474147347300207730ustar00rootroot00000000000000/* * framework-pkcs15.c: PKCS#15 framework and related objects * * Copyright (C) 2002 Timo Teräs * * 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 "sc-pkcs11.h" #ifdef USE_PKCS15_INIT #include "pkcs15init/pkcs15-init.h" /* * Deal with uninitialized cards */ static CK_RV pkcs15init_bind(struct sc_pkcs11_card *p11card, struct sc_app_info *app_info) { 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; return sc_to_cryptoki_error(rc, NULL); } 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; } static CK_RV pkcs15init_create_tokens(struct sc_pkcs11_card *p11card, struct sc_app_info *app_info) { struct sc_profile *profile; struct sc_pkcs11_slot *slot; CK_RV rc; if (!p11card) return CKR_TOKEN_NOT_RECOGNIZED; profile = (struct sc_profile *) p11card->fws_data[0]; rc = slot_allocate(&slot, p11card); if (rc == CKR_OK) { CK_TOKEN_INFO_PTR pToken = &slot->token_info; const char *string; slot->slot_info.flags |= CKF_TOKEN_PRESENT; strcpy_bp(pToken->model, "PKCS #15 SCard", 16); sc_pkcs15init_get_manufacturer(profile, &string); if (!string) string = "Unknown"; strcpy_bp(pToken->manufacturerID, string, 32); sc_pkcs15init_get_serial(profile, &string); if (!string) string = ""; strcpy_bp(pToken->serialNumber, string, 16); pToken->ulMaxSessionCount = CK_EFFECTIVELY_INFINITE; pToken->ulSessionCount = 0; /* FIXME */ pToken->ulMaxRwSessionCount = CK_EFFECTIVELY_INFINITE; pToken->ulRwSessionCount = 0; /* FIXME */ pToken->ulTotalPublicMemory = CK_UNAVAILABLE_INFORMATION; pToken->ulFreePublicMemory = CK_UNAVAILABLE_INFORMATION; pToken->ulTotalPrivateMemory = CK_UNAVAILABLE_INFORMATION; pToken->ulFreePrivateMemory = CK_UNAVAILABLE_INFORMATION; pToken->hardwareVersion.major = 0; pToken->hardwareVersion.minor = 0; pToken->firmwareVersion.major = 0; pToken->firmwareVersion.minor = 0; } return CKR_OK; } static CK_RV pkcs15init_release_token(struct sc_pkcs11_card *p11card, void *ptr) { return CKR_OK; } static CK_RV pkcs15init_login(struct sc_pkcs11_slot *slot, CK_USER_TYPE user, CK_CHAR_PTR pin, CK_ULONG pinLength) { return CKR_CRYPTOKI_NOT_INITIALIZED; } static CK_RV pkcs15init_logout(struct sc_pkcs11_slot *slot) { return CKR_CRYPTOKI_NOT_INITIALIZED; } static CK_RV pkcs15init_change_pin(struct sc_pkcs11_slot *slot, CK_CHAR_PTR oldPin, CK_ULONG oldPinLength, CK_CHAR_PTR newPin, CK_ULONG newPinLength) { return CKR_CRYPTOKI_NOT_INITIALIZED; } static CK_RV pkcs15init_initialize(struct sc_pkcs11_slot *pslot, void *ptr, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen, CK_UTF8CHAR_PTR pLabel) { struct sc_pkcs11_card *p11card = pslot->p11card; struct sc_profile *profile; struct sc_pkcs15init_initargs args; struct sc_pkcs11_slot *slot; 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; args.so_puk = pPin; args.so_puk_len = ulPinLen; args.label = (const char *) pLabel; rc = sc_pkcs15init_add_app(p11card->card, profile, &args); if (rc < 0) return sc_to_cryptoki_error(rc, NULL); /* Change the binding from the pkcs15init framework * to the pkcs15 framework on the fly. * First, try to bind pkcs15 framework */ if ((rv = framework_pkcs15.bind(p11card, NULL)) != CKR_OK) { /* whoops, bad */ p11card->fws_data[0] = profile; return rv; } /* Change the function vector to the standard pkcs15 ops */ p11card->framework = &framework_pkcs15; /* Loop over all slots belonging to this card, and fix up * the flags. */ for (id = 0; slot_get_slot(id, &slot) == CKR_OK; id++) { if (slot->p11card == p11card) slot->token_info.flags |= CKF_TOKEN_INITIALIZED; if (slot->p11card->card->caps & SC_CARD_CAP_RNG) slot->token_info.flags |= CKF_RNG; } sc_pkcs15init_unbind(profile); return CKR_OK; } struct sc_pkcs11_framework_ops framework_pkcs15init = { pkcs15init_bind, pkcs15init_unbind, pkcs15init_create_tokens, pkcs15init_release_token, pkcs15init_login, pkcs15init_logout, pkcs15init_change_pin, pkcs15init_initialize, NULL, /* init_pin */ NULL, /* create_object */ NULL, /* gen_keypair */ NULL /* get_random */ }; #else /* ifdef USE_PKCS15_INIT */ struct sc_pkcs11_framework_ops framework_pkcs15init = { NULL, /* bind */ NULL, /* unbind */ NULL, /* create_tokens */ NULL, /* release_tokens */ NULL, /* login */ NULL, /* logout */ NULL, /* change_pin */ NULL, /* init_token */ NULL, /* init_pin */ NULL, /* create_object */ NULL, /* gen_keypair */ NULL /* get_random */ }; #endif OpenSC-0.26.1/src/pkcs11/mechanism.c000066400000000000000000001405701474147347300167550ustar00rootroot00000000000000/* * Generic handling of PKCS11 mechanisms * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include #include #include "common/compat_overflow.h" #include "common/constant-time.h" #include "sc-pkcs11.h" /* Also used for verification data */ struct hash_signature_info { CK_MECHANISM_TYPE mech; CK_MECHANISM_TYPE hash_mech; CK_MECHANISM_TYPE sign_mech; sc_pkcs11_mechanism_type_t *hash_type; }; /* Also used for verification and decryption data */ struct operation_data { struct sc_pkcs11_object *key; struct hash_signature_info *info; 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 **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++] = 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, CK_FLAGS flags) { sc_pkcs11_mechanism_type_t *mt; unsigned int n; for (n = 0; n < p11card->nmechanisms; n++) { mt = p11card->mechanisms[n]; if (mt && mt->mech == mech && ((mt->mech_info.flags & flags) == flags)) return mt; } return NULL; } /* * Query mechanisms. * All of this is greatly simplified by having the framework * register all supported mechanisms at initialization * time. */ CK_RV sc_pkcs11_get_mechanism_list(struct sc_pkcs11_card *p11card, CK_MECHANISM_TYPE_PTR pList, CK_ULONG_PTR pulCount) { sc_pkcs11_mechanism_type_t *mt; unsigned int n, count = 0; CK_RV rv; if (!p11card) return CKR_TOKEN_NOT_PRESENT; for (n = 0; n < p11card->nmechanisms; n++) { if (!(mt = p11card->mechanisms[n])) continue; if (pList && count < *pulCount) pList[count] = mt->mech; count++; } rv = CKR_OK; if (pList && count > *pulCount) rv = CKR_BUFFER_TOO_SMALL; *pulCount = count; return rv; } CK_RV sc_pkcs11_get_mechanism_info(struct sc_pkcs11_card *p11card, CK_MECHANISM_TYPE mechanism, CK_MECHANISM_INFO_PTR pInfo) { sc_pkcs11_mechanism_type_t *mt; if (!(mt = sc_pkcs11_find_mechanism(p11card, mechanism, 0))) return CKR_MECHANISM_INVALID; memcpy(pInfo, &mt->mech_info, sizeof(*pInfo)); return CKR_OK; } /* * Create/destroy operation handle */ sc_pkcs11_operation_t * sc_pkcs11_new_operation(sc_pkcs11_session_t *session, sc_pkcs11_mechanism_type_t *type) { sc_pkcs11_operation_t *res; res = calloc(1, type->obj_size); if (res) { res->session = session; res->type = type; } return res; } void sc_pkcs11_release_operation(sc_pkcs11_operation_t **ptr) { sc_pkcs11_operation_t *operation = *ptr; if (!operation) return; if (operation->type && operation->type->release) operation->type->release(operation); memset(operation, 0, sizeof(*operation)); free(operation); *ptr = NULL; } CK_RV sc_pkcs11_md_init(struct sc_pkcs11_session *session, CK_MECHANISM_PTR pMechanism) { struct sc_pkcs11_card *p11card; sc_pkcs11_operation_t *operation; sc_pkcs11_mechanism_type_t *mt; CK_RV rv; LOG_FUNC_CALLED(context); if (!session || !session->slot || !(p11card = session->slot->p11card)) LOG_FUNC_RETURN(context, CKR_ARGUMENTS_BAD); /* See if we support this mechanism type */ mt = sc_pkcs11_find_mechanism(p11card, pMechanism->mechanism, CKF_DIGEST); if (mt == NULL) LOG_FUNC_RETURN(context, CKR_MECHANISM_INVALID); rv = session_start_operation(session, SC_PKCS11_OPERATION_DIGEST, mt, &operation); if (rv != CKR_OK) LOG_FUNC_RETURN(context, (int) rv); memcpy(&operation->mechanism, pMechanism, sizeof(CK_MECHANISM)); rv = mt->md_init(operation); if (rv != CKR_OK) session_stop_operation(session, SC_PKCS11_OPERATION_DIGEST); LOG_FUNC_RETURN(context, (int) rv); } CK_RV sc_pkcs11_md_update(struct sc_pkcs11_session *session, CK_BYTE_PTR pData, CK_ULONG ulDataLen) { sc_pkcs11_operation_t *op; CK_RV rv; rv = session_get_operation(session, SC_PKCS11_OPERATION_DIGEST, &op); if (rv != CKR_OK) goto done; rv = op->type->md_update(op, pData, ulDataLen); done: if (rv != CKR_OK) session_stop_operation(session, SC_PKCS11_OPERATION_DIGEST); LOG_FUNC_RETURN(context, (int) rv); } CK_RV sc_pkcs11_md_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_DIGEST, &op); if (rv != CKR_OK) LOG_FUNC_RETURN(context, (int) rv); /* This is a request for the digest length */ if (pData == NULL) *pulDataLen = 0; rv = op->type->md_final(op, pData, pulDataLen); if (rv == CKR_BUFFER_TOO_SMALL) LOG_FUNC_RETURN(context, pData == NULL ? CKR_OK : CKR_BUFFER_TOO_SMALL); session_stop_operation(session, SC_PKCS11_OPERATION_DIGEST); LOG_FUNC_RETURN(context, (int) rv); } /* * Initialize a signing context. When we get here, we know * the key object is capable of signing _something_ */ CK_RV sc_pkcs11_sign_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; LOG_FUNC_CALLED(context); if (!session || !session->slot || !(p11card = session->slot->p11card)) LOG_FUNC_RETURN(context, CKR_ARGUMENTS_BAD); /* See if we support this mechanism type */ sc_log(context, "mechanism 0x%lX, key-type 0x%lX", pMechanism->mechanism, key_type); mt = sc_pkcs11_find_mechanism(p11card, pMechanism->mechanism, CKF_SIGN); if (mt == NULL) LOG_FUNC_RETURN(context, 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_SIGN, mt, &operation); if (rv != CKR_OK) 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, (int) rv); } CK_RV sc_pkcs11_sign_update(struct sc_pkcs11_session *session, CK_BYTE_PTR pData, CK_ULONG ulDataLen) { sc_pkcs11_operation_t *op; 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, (int) rv); if (op->type->sign_update == NULL) { rv = CKR_KEY_TYPE_INCONSISTENT; goto done; } rv = op->type->sign_update(op, pData, ulDataLen); done: if (rv != CKR_OK) session_stop_operation(session, SC_PKCS11_OPERATION_SIGN); LOG_FUNC_RETURN(context, (int) rv); } CK_RV sc_pkcs11_sign_final(struct sc_pkcs11_session *session, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) { sc_pkcs11_operation_t *op; 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, (int) rv); /* Bail out for signature mechanisms that don't do hashing */ if (op->type->sign_final == NULL) { rv = CKR_KEY_TYPE_INCONSISTENT; goto done; } rv = op->type->sign_final(op, pSignature, pulSignatureLen); done: if (rv != CKR_BUFFER_TOO_SMALL && pSignature != NULL) session_stop_operation(session, SC_PKCS11_OPERATION_SIGN); 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; CK_RV rv; rv = session_get_operation(session, SC_PKCS11_OPERATION_SIGN, &op); if (rv != CKR_OK) LOG_FUNC_RETURN(context, (int) rv); /* Bail out for signature mechanisms that don't do hashing */ if (op->type->sign_size == NULL) { rv = CKR_KEY_TYPE_INCONSISTENT; goto done; } rv = op->type->sign_size(op, pLength); done: if (rv != CKR_OK) session_stop_operation(session, SC_PKCS11_OPERATION_SIGN); LOG_FUNC_RETURN(context, (int) rv); } /* * Initialize a signature operation */ static CK_RV sc_pkcs11_signature_init(sc_pkcs11_operation_t *operation, struct sc_pkcs11_object *key) { struct hash_signature_info *info; struct operation_data *data; CK_RV rv; int can_do_it = 0; LOG_FUNC_CALLED(context); if (!(data = new_operation_data())) LOG_FUNC_RETURN(context, CKR_HOST_MEMORY); data->info = NULL; data->key = key; if (key->ops->can_do) { rv = key->ops->can_do(operation->session, key, operation->type->mech, CKF_SIGN); if (rv == CKR_OK) { /* Mechanism recognised and can be performed by pkcs#15 card */ can_do_it = 1; } else if (rv == CKR_FUNCTION_NOT_SUPPORTED) { /* Mechanism not recognised by pkcs#15 card */ can_do_it = 0; } else { /* Mechanism recognised but cannot be performed by pkcs#15 card, or some general error. */ 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; if (info != NULL && !can_do_it) { /* Initialize hash operation */ data->md = sc_pkcs11_new_operation(operation->session, info->hash_type); if (data->md == NULL) rv = CKR_HOST_MEMORY; else rv = info->hash_type->md_init(data->md); if (rv != CKR_OK) { sc_pkcs11_release_operation(&data->md); operation_data_release(data); LOG_FUNC_RETURN(context, (int) rv); } data->info = info; } operation->priv_data = data; LOG_FUNC_RETURN(context, CKR_OK); } static CK_RV sc_pkcs11_signature_update(sc_pkcs11_operation_t *operation, CK_BYTE_PTR pPart, CK_ULONG ulPartLen) { struct operation_data *data; CK_RV rv; LOG_FUNC_CALLED(context); sc_log(context, "data part length %li", ulPartLen); data = (struct operation_data *)operation->priv_data; if (data->md) { rv = data->md->type->md_update(data->md, pPart, ulPartLen); LOG_FUNC_RETURN(context, (int) rv); } /* This signature mechanism operates on the raw data */ 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 operation_data *data; CK_RV rv; LOG_FUNC_CALLED(context); data = (struct operation_data *)operation->priv_data; if (data->md) { sc_pkcs11_operation_t *md = data->md; CK_BYTE hash[64]; CK_ULONG len = sizeof(hash); 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, (int) rv); rv = signature_data_buffer_append(data, hash, len); if (rv != CKR_OK) LOG_FUNC_RETURN(context, (int) rv); } rv = data->key->ops->sign(operation->session, data->key, &operation->mechanism, data->buffer, data->buffer_len, pSignature, pulSignatureLen); LOG_FUNC_RETURN(context, (int) rv); } static CK_RV sc_pkcs11_signature_size(sc_pkcs11_operation_t *operation, CK_ULONG_PTR pLength) { struct sc_pkcs11_object *key; CK_ATTRIBUTE attr = { CKA_MODULUS_BITS, pLength, sizeof(*pLength) }; CK_KEY_TYPE key_type; CK_ATTRIBUTE attr_key_type = { CKA_KEY_TYPE, &key_type, sizeof(key_type) }; CK_RV rv; 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. * So should do switch(key_type) * and then get what ever attributes are needed. */ rv = key->ops->get_attribute(operation->session, key, &attr_key_type); if (rv == CKR_OK) { switch(key_type) { case CKK_RSA: rv = key->ops->get_attribute(operation->session, key, &attr); /* convert bits to bytes */ if (rv == CKR_OK) *pLength = (*pLength + 7) / 8; 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); if (rv == CKR_OK) *pLength = ((*pLength + 7)/8) * 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; break; default: rv = CKR_MECHANISM_INVALID; } } LOG_FUNC_RETURN(context, (int) rv); } static void sc_pkcs11_operation_release(sc_pkcs11_operation_t *operation) { if (!operation) return; operation_data_release(operation->priv_data); } #ifdef ENABLE_OPENSSL /* * Initialize a verify context. When we get here, we know * the key object is capable of verifying _something_ */ CK_RV sc_pkcs11_verif_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_VERIFY); 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_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) session_stop_operation(session, SC_PKCS11_OPERATION_VERIFY); return rv; } CK_RV sc_pkcs11_verif_update(struct sc_pkcs11_session *session, CK_BYTE_PTR pData, CK_ULONG ulDataLen) { sc_pkcs11_operation_t *op; CK_RV rv; rv = session_get_operation(session, SC_PKCS11_OPERATION_VERIFY, &op); if (rv != CKR_OK) return rv; if (op->type->verif_update == NULL) { rv = CKR_KEY_TYPE_INCONSISTENT; goto done; } rv = op->type->verif_update(op, pData, ulDataLen); done: if (rv != CKR_OK) session_stop_operation(session, SC_PKCS11_OPERATION_VERIFY); return rv; } CK_RV sc_pkcs11_verif_final(struct sc_pkcs11_session *session, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen) { sc_pkcs11_operation_t *op; CK_RV rv; rv = session_get_operation(session, SC_PKCS11_OPERATION_VERIFY, &op); if (rv != CKR_OK) return rv; if (op->type->verif_final == NULL) { rv = CKR_KEY_TYPE_INCONSISTENT; goto done; } rv = op->type->verif_final(op, pSignature, ulSignatureLen); done: session_stop_operation(session, SC_PKCS11_OPERATION_VERIFY); return rv; } /* * Initialize a verify operation */ static CK_RV sc_pkcs11_verify_init(sc_pkcs11_operation_t *operation, struct sc_pkcs11_object *key) { struct hash_signature_info *info; struct operation_data *data; CK_RV rv; if (!(data = new_operation_data())) return CKR_HOST_MEMORY; data->info = NULL; data->key = key; if (key->ops->can_do) { rv = key->ops->can_do(operation->session, key, operation->type->mech, CKF_SIGN); 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); } } /* 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); } } /* If this is a verify with hash operation, set up the * hash operation */ info = (struct hash_signature_info *) operation->type->mech_data; if (info != NULL) { /* Initialize hash operation */ data->md = sc_pkcs11_new_operation(operation->session, info->hash_type); if (data->md == NULL) rv = CKR_HOST_MEMORY; else rv = info->hash_type->md_init(data->md); if (rv != CKR_OK) { sc_pkcs11_release_operation(&data->md); free(data); return rv; } data->info = info; } operation->priv_data = data; return CKR_OK; } static CK_RV sc_pkcs11_verify_update(sc_pkcs11_operation_t *operation, CK_BYTE_PTR pPart, CK_ULONG ulPartLen) { struct operation_data *data; data = (struct operation_data *)operation->priv_data; if (data->md) { sc_pkcs11_operation_t *md = data->md; return md->type->md_update(md, pPart, ulPartLen); } /* This verification mechanism operates on the raw data */ 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 operation_data *data; struct sc_pkcs11_object *key; unsigned char *pubkey_value = NULL; CK_KEY_TYPE key_type; CK_BYTE params[9 /* GOST_PARAMS_ENCODED_OID_SIZE */] = { 0 }; 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)}; CK_RV rv; data = (struct operation_data *)operation->priv_data; if (pSignature == NULL) return CKR_ARGUMENTS_BAD; key = data->key; rv = key->ops->get_attribute(operation->session, key, &attr_key_type); if (rv != CKR_OK) return rv; if (key_type != CKK_GOSTR3410) attr.type = CKA_SPKI; rv = key->ops->get_attribute(operation->session, key, &attr); if (rv != CKR_OK) return rv; pubkey_value = calloc(1, attr.ulValueLen); if (!pubkey_value) { rv = CKR_HOST_MEMORY; goto done; } attr.pValue = pubkey_value; rv = key->ops->get_attribute(operation->session, key, &attr); if (rv != CKR_OK) goto done; if (key_type == CKK_GOSTR3410) { rv = key->ops->get_attribute(operation->session, key, &attr_key_params); if (rv != CKR_OK) goto done; } rv = sc_pkcs11_verify_data(pubkey_value, attr.ulValueLen, params, sizeof(params), &operation->mechanism, data->md, data->buffer, data->buffer_len, pSignature, ulSignatureLen); done: free(pubkey_value); 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); 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 * the key object is capable of decrypting _something_ */ CK_RV sc_pkcs11_decr_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_DECRYPT); 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_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); return rv; } CK_RV sc_pkcs11_decr(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(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_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; } /* Derive one key from another, and return results in created object */ CK_RV sc_pkcs11_deri(struct sc_pkcs11_session *session, CK_MECHANISM_PTR pMechanism, struct sc_pkcs11_object * basekey, CK_KEY_TYPE key_type, CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hdkey, struct sc_pkcs11_object * dkey) { struct sc_pkcs11_card *p11card; sc_pkcs11_operation_t *operation; sc_pkcs11_mechanism_type_t *mt; CK_BYTE_PTR keybuf = NULL; CK_ULONG ulDataLen = 0; CK_ATTRIBUTE template[] = { {CKA_VALUE, keybuf, 0} }; 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_DERIVE); 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_DERIVE, mt, &operation); if (rv != CKR_OK) return rv; memcpy(&operation->mechanism, pMechanism, sizeof(CK_MECHANISM)); /* 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 secret key CKA_VALUE */ ulDataLen = 0; rv = operation->type->derive(operation, basekey, pMechanism->pParameter, pMechanism->ulParameterLen, NULL, &ulDataLen); if (rv != CKR_OK) goto out; if (ulDataLen > 0) keybuf = calloc(1,ulDataLen); else keybuf = calloc(1,8); /* pass in dummy buffer */ if (!keybuf) { rv = CKR_HOST_MEMORY; goto out; } /* Now do the actual derivation */ rv = operation->type->derive(operation, basekey, pMechanism->pParameter, pMechanism->ulParameterLen, keybuf, &ulDataLen); if (rv != CKR_OK) goto out; /* add the CKA_VALUE attribute to the template if it was returned * if not assume it is on the card... * But for now PIV with ECDH returns the generic key data * TODO need to support truncation, if CKA_VALUE_LEN < ulDataLem */ if (ulDataLen > 0) { template[0].pValue = keybuf; template[0].ulValueLen = ulDataLen; dkey->ops->set_attribute(session, dkey, &template[0]); memset(keybuf,0,ulDataLen); } out: session_stop_operation(session, SC_PKCS11_OPERATION_DERIVE); if (keybuf) free(keybuf); 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 */ static CK_RV sc_pkcs11_decrypt_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_DECRYPT); 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->decrypt(operation->session, key, &operation->mechanism, NULL, 0, NULL, NULL); } static CK_RV sc_pkcs11_decrypt(sc_pkcs11_operation_t *operation, CK_BYTE_PTR pEncryptedData, CK_ULONG ulEncryptedDataLen, CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen) { 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; ulDataLen = pulDataLen ? *pulDataLen : 0; ulLastDataLen = ulDataLen; data = (struct operation_data *)operation->priv_data; key = data->key; /* 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 sc_pkcs11_derive(sc_pkcs11_operation_t *operation, struct sc_pkcs11_object *basekey, CK_BYTE_PTR pmechParam, CK_ULONG ulmechParamLen, CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen) { return basekey->ops->derive(operation->session, basekey, &operation->mechanism, pmechParam, ulmechParamLen, 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 */ sc_pkcs11_mechanism_type_t * 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), CK_RV (*copy_priv_data)(const void *mech_data, void **new_data)) { sc_pkcs11_mechanism_type_t *mt; mt = calloc(1, sizeof(*mt)); if (mt == NULL) return mt; mt->mech = mech; mt->mech_info = *pInfo; 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_operation_release; if (pInfo->flags & CKF_SIGN) { mt->sign_init = sc_pkcs11_signature_init; mt->sign_update = sc_pkcs11_signature_update; mt->sign_final = sc_pkcs11_signature_final; mt->sign_size = sc_pkcs11_signature_size; #ifdef ENABLE_OPENSSL mt->verif_init = sc_pkcs11_verify_init; mt->verif_update = sc_pkcs11_verify_update; mt->verif_final = sc_pkcs11_verify_final; #endif } if (pInfo->flags & CKF_WRAP) { mt->wrap = sc_pkcs11_wrap_operation; } if (pInfo->flags & CKF_UNWRAP) { mt->unwrap = sc_pkcs11_unwrap_operation; } if (pInfo->flags & CKF_DERIVE) { mt->derive = sc_pkcs11_derive; } 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 */ CK_RV sc_pkcs11_register_generic_mechanisms(struct sc_pkcs11_card *p11card) { #ifdef ENABLE_OPENSSL sc_pkcs11_register_openssl_mechanisms(p11card); #endif return CKR_OK; } 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 */ CK_RV sc_pkcs11_register_sign_and_hash_mechanism(struct sc_pkcs11_card *p11card, CK_MECHANISM_TYPE mech, CK_MECHANISM_TYPE hash_mech, sc_pkcs11_mechanism_type_t *sign_type) { sc_pkcs11_mechanism_type_t *hash_type, *new_type; struct hash_signature_info *info; CK_MECHANISM_INFO mech_info; CK_RV rv; LOG_FUNC_CALLED(p11card->card->ctx); sc_log(p11card->card->ctx, "mech = %lx, hash_mech = %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))) 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, CKR_HOST_MEMORY); info->mech = mech; 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_types[0], info, free_info, copy_hash_signature_info); if (!new_type) { free_info(info); LOG_FUNC_RETURN(p11card->card->ctx, CKR_HOST_MEMORY); } rv = sc_pkcs11_register_mechanism(p11card, new_type, NULL); sc_pkcs11_free_mechanism(&new_type); LOG_FUNC_RETURN(p11card->card->ctx, (int)rv); } OpenSC-0.26.1/src/pkcs11/misc.c000066400000000000000000000335541474147347300157470ustar00rootroot00000000000000/* * misc.c: Miscellaneous PKCS#11 library helper functions * * Copyright (C) 2002 Timo Teräs * * 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/constant-time.h" #include "sc-pkcs11.h" #define DUMP_TEMPLATE_MAX 32 struct sc_to_cryptoki_error_conversion { const char *context; int sc_error; CK_RV ck_error; }; static struct sc_to_cryptoki_error_conversion sc_to_cryptoki_error_map[] = { { "C_GenerateKeyPair", SC_ERROR_INVALID_PIN_LENGTH, CKR_GENERAL_ERROR }, { "C_Sign", SC_ERROR_NOT_ALLOWED, CKR_FUNCTION_FAILED}, { "C_Decrypt", SC_ERROR_NOT_ALLOWED, CKR_FUNCTION_FAILED}, {NULL, 0, 0} }; void strcpy_bp(u8 * dst, const char *src, size_t dstsize) { if (!dst || !dstsize) return; memset((char *)dst, ' ', dstsize); if (src) { size_t src_len = strlen(src); 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); } } } static CK_RV sc_to_cryptoki_error_common(int rc) { sc_log(context, "libopensc return value: %d (%s)\n", rc, sc_strerror(rc)); switch (rc) { case SC_SUCCESS: return CKR_OK; case SC_ERROR_NOT_SUPPORTED: return CKR_FUNCTION_NOT_SUPPORTED; case SC_ERROR_OUT_OF_MEMORY: return CKR_HOST_MEMORY; case SC_ERROR_PIN_CODE_INCORRECT: return CKR_PIN_INCORRECT; case SC_ERROR_AUTH_METHOD_BLOCKED: return CKR_PIN_LOCKED; case SC_ERROR_BUFFER_TOO_SMALL: return CKR_BUFFER_TOO_SMALL; case SC_ERROR_CARD_NOT_PRESENT: return CKR_TOKEN_NOT_PRESENT; case SC_ERROR_INVALID_CARD: case SC_ERROR_WRONG_CARD: case SC_ERROR_NO_CARD_SUPPORT: return CKR_TOKEN_NOT_RECOGNIZED; case SC_ERROR_WRONG_LENGTH: return CKR_DATA_LEN_RANGE; case SC_ERROR_INVALID_PIN_LENGTH: return CKR_PIN_LEN_RANGE; case SC_ERROR_KEYPAD_CANCELLED: case SC_ERROR_KEYPAD_TIMEOUT: return CKR_FUNCTION_CANCELED; case SC_ERROR_CARD_REMOVED: return CKR_DEVICE_REMOVED; case SC_ERROR_SECURITY_STATUS_NOT_SATISFIED: return CKR_USER_NOT_LOGGED_IN; case SC_ERROR_KEYPAD_PIN_MISMATCH: return CKR_PIN_INVALID; case SC_ERROR_INVALID_ARGUMENTS: return CKR_ARGUMENTS_BAD; case SC_ERROR_INVALID_DATA: case SC_ERROR_INCORRECT_PARAMETERS: return CKR_DATA_INVALID; case SC_ERROR_CARD_UNRESPONSIVE: case SC_ERROR_READER_LOCKED: return CKR_DEVICE_ERROR; case SC_ERROR_READER_DETACHED: return CKR_TOKEN_NOT_PRESENT; /* Maybe CKR_DEVICE_REMOVED ? */ case SC_ERROR_NOT_ENOUGH_MEMORY: 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; } CK_RV sc_to_cryptoki_error(int rc, const char *ctx) { if (ctx) { int ii; for (ii = 0; sc_to_cryptoki_error_map[ii].context; ii++) { if (sc_to_cryptoki_error_map[ii].sc_error != rc) continue; if (strcmp(sc_to_cryptoki_error_map[ii].context, ctx)) continue; return sc_to_cryptoki_error_map[ii].ck_error; } } return sc_to_cryptoki_error_common(rc); } struct sc_pkcs11_login { CK_USER_TYPE userType; CK_CHAR_PTR pPin; CK_ULONG ulPinLen; }; CK_RV restore_login_state(struct sc_pkcs11_slot *slot) { CK_RV r = CKR_OK; if (sc_pkcs11_conf.atomic && slot) { if (list_iterator_start(&slot->logins)) { struct sc_pkcs11_login *login = list_iterator_next(&slot->logins); while (login && slot->p11card && slot->p11card->framework) { r = slot->p11card->framework->login(slot, login->userType, login->pPin, login->ulPinLen); if (r != CKR_OK) break; login = list_iterator_next(&slot->logins); } list_iterator_stop(&slot->logins); } } return r; } CK_RV reset_login_state(struct sc_pkcs11_slot *slot, CK_RV rv) { if (slot) { if (sc_pkcs11_conf.atomic && slot->p11card && slot->p11card->framework) { slot->p11card->framework->logout(slot); } if (constant_time_eq_s(rv, CKR_USER_NOT_LOGGED_IN)) { slot->login_user = -1; pop_all_login_states(slot); } } return rv; } CK_RV push_login_state(struct sc_pkcs11_slot *slot, CK_USER_TYPE userType, CK_CHAR_PTR pPin, CK_ULONG ulPinLen) { CK_RV r = CKR_HOST_MEMORY; struct sc_pkcs11_login *login = NULL; if (!sc_pkcs11_conf.atomic || !slot) { return CKR_OK; } login = (struct sc_pkcs11_login *) calloc(1, sizeof *login); if (login == NULL) { goto err; } if (pPin && ulPinLen) { login->pPin = sc_mem_secure_alloc((sizeof *pPin)*ulPinLen); if (login->pPin == NULL) { goto err; } memcpy(login->pPin, pPin, (sizeof *pPin)*ulPinLen); login->ulPinLen = ulPinLen; } login->userType = userType; if (0 > list_append(&slot->logins, login)) { goto err; } login = NULL; r = CKR_OK; err: if (login) { if (login->pPin) { sc_mem_secure_clear_free(login->pPin, login->ulPinLen); } free(login); } return r; } void pop_login_state(struct sc_pkcs11_slot *slot) { if (slot) { unsigned int size = list_size(&slot->logins); if (size > 0) { struct sc_pkcs11_login *login = list_get_at(&slot->logins, size-1); if (login) { sc_mem_secure_clear_free(login->pPin, login->ulPinLen); free(login); } if (0 > list_delete_at(&slot->logins, size-1)) sc_log(context, "Error deleting login state"); } } } 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_secure_clear_free(login->pPin, login->ulPinLen); free(login); login = list_fetch(&slot->logins); } } } /* Session manipulation */ CK_RV session_start_operation(struct sc_pkcs11_session * session, int type, sc_pkcs11_mechanism_type_t * mech, struct sc_pkcs11_operation ** operation) { sc_pkcs11_operation_t *op; if (context == NULL) return CKR_CRYPTOKI_NOT_INITIALIZED; LOG_FUNC_CALLED(context); sc_log(context, "Session 0x%lx, type %d", session->handle, type); if (type < 0 || type >= SC_PKCS11_OPERATION_MAX) return CKR_ARGUMENTS_BAD; if (session->operation[type] != NULL) return CKR_OPERATION_ACTIVE; if (!(op = sc_pkcs11_new_operation(session, mech))) return CKR_HOST_MEMORY; session->operation[type] = op; if (operation) *operation = op; return CKR_OK; } CK_RV session_get_operation(struct sc_pkcs11_session * session, int type, sc_pkcs11_operation_t ** operation) { sc_pkcs11_operation_t *op; LOG_FUNC_CALLED(context); if (type < 0 || type >= SC_PKCS11_OPERATION_MAX) return CKR_ARGUMENTS_BAD; if (!(op = session->operation[type])) return CKR_OPERATION_NOT_INITIALIZED; if (operation) *operation = op; return CKR_OK; } CK_RV session_stop_operation(struct sc_pkcs11_session * session, int type) { if (type < 0 || type >= SC_PKCS11_OPERATION_MAX) return CKR_ARGUMENTS_BAD; if (session->operation[type] == NULL) return CKR_OPERATION_NOT_INITIALIZED; sc_pkcs11_release_operation(&session->operation[type]); return CKR_OK; } CK_RV attr_extract(CK_ATTRIBUTE_PTR pAttr, void *ptr, size_t * sizep) { size_t size; if (sizep) { size = *sizep; if (size < pAttr->ulValueLen) return CKR_ATTRIBUTE_VALUE_INVALID; *sizep = pAttr->ulValueLen; } else { switch (pAttr->type) { case CKA_CLASS: size = sizeof(CK_OBJECT_CLASS); break; case CKA_KEY_TYPE: size = sizeof(CK_KEY_TYPE); break; case CKA_PRIVATE: case CKA_TOKEN: size = sizeof(CK_BBOOL); break; case CKA_CERTIFICATE_TYPE: size = sizeof(CK_CERTIFICATE_TYPE); break; case CKA_VALUE_LEN: case CKA_MODULUS_BITS: size = sizeof(CK_ULONG); break; case CKA_OBJECT_ID: size = sizeof(struct sc_object_id); break; default: return CKR_FUNCTION_FAILED; } if (size != pAttr->ulValueLen) return CKR_ATTRIBUTE_VALUE_INVALID; } memcpy(ptr, pAttr->pValue, pAttr->ulValueLen); return CKR_OK; } CK_RV attr_find(CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_ULONG type, void *ptr, size_t * sizep) { unsigned int n; for (n = 0; n < ulCount; n++, pTemplate++) { if (pTemplate->type == type) break; } if (n >= ulCount) return CKR_TEMPLATE_INCOMPLETE; return attr_extract(pTemplate, ptr, sizep); } CK_RV attr_find2(CK_ATTRIBUTE_PTR pTemp1, CK_ULONG ulCount1, CK_ATTRIBUTE_PTR pTemp2, CK_ULONG ulCount2, CK_ULONG type, void *ptr, size_t * sizep) { CK_RV rv; rv = attr_find(pTemp1, ulCount1, type, ptr, sizep); if (rv != CKR_OK) rv = attr_find(pTemp2, ulCount2, type, ptr, sizep); return rv; } CK_RV attr_find_and_allocate_ptr(CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_ULONG type, void **out, size_t *out_len) { void *ptr; size_t len; CK_RV rv; if (!out || !out_len) return CKR_ARGUMENTS_BAD; len = *out_len; rv = attr_find_ptr(pTemplate, ulCount, type, &ptr, &len); if (rv != CKR_OK) return rv; *out = calloc(1, len); if (*out == NULL) return CKR_HOST_MEMORY; memcpy(*out, ptr, len); *out_len = len; return CKR_OK; } CK_RV attr_find_ptr(CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_ULONG type, void **ptr, size_t * sizep) { unsigned int n; for (n = 0; n < ulCount; n++, pTemplate++) { if (pTemplate->type == type) break; } if (n >= ulCount) return CKR_TEMPLATE_INCOMPLETE; if (sizep) *sizep = pTemplate->ulValueLen; *ptr = pTemplate->pValue; return CKR_OK; } CK_RV attr_find_ptr2(CK_ATTRIBUTE_PTR pTemp1, CK_ULONG ulCount1, CK_ATTRIBUTE_PTR pTemp2, CK_ULONG ulCount2, CK_ULONG type, void **ptr, size_t * sizep) { CK_RV rv; rv = attr_find_ptr(pTemp1, ulCount1, type, ptr, sizep); if (rv != CKR_OK) rv = attr_find_ptr(pTemp2, ulCount2, type, ptr, sizep); return rv; } CK_RV attr_find_var(CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_ULONG type, void *ptr, size_t * sizep) { unsigned int n; for (n = 0; n < ulCount; n++, pTemplate++) { if (pTemplate->type == type) break; } if (n >= ulCount) return CKR_TEMPLATE_INCOMPLETE; 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; char *unblock_style = NULL; char *create_slots_for_pins = NULL, *op, *tmp; /* Set defaults */ conf->max_virtual_slots = 16; 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->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->create_slots_flags = SC_PKCS11_SLOT_CREATE_ALL; conf_block = sc_get_conf_block(ctx, "pkcs11", NULL, 1); if (!conf_block) 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->atomic = scconf_get_bool(conf_block, "atomic", conf->atomic); if (conf->atomic) conf->lock_login = 1; conf->lock_login = scconf_get_bool(conf_block, "lock_login", conf->lock_login); conf->init_sloppy = scconf_get_bool(conf_block, "init_sloppy", conf->init_sloppy); unblock_style = (char *)scconf_get_str(conf_block, "user_pin_unblock_style", NULL); if (unblock_style && !strcmp(unblock_style, "set_pin_in_unlogged_session")) conf->pin_unblock_style = SC_PKCS11_PIN_UNBLOCK_UNLOGGED_SETPIN; else if (unblock_style && !strcmp(unblock_style, "set_pin_in_specific_context")) conf->pin_unblock_style = SC_PKCS11_PIN_UNBLOCK_SCONTEXT_SETPIN; else if (unblock_style && !strcmp(unblock_style, "init_pin_in_so_session")) 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); create_slots_for_pins = (char *)scconf_get_str(conf_block, "create_slots_for_pins", "all"); conf->create_slots_flags = 0; tmp = strdup(create_slots_for_pins); op = strtok(tmp, " ,"); while (op) { if (!strcmp(op, "user")) conf->create_slots_flags |= SC_PKCS11_SLOT_FOR_PIN_USER; else if (!strcmp(op, "sign")) conf->create_slots_flags |= SC_PKCS11_SLOT_FOR_PIN_SIGN; else if (!strcmp(op, "all")) conf->create_slots_flags |= SC_PKCS11_SLOT_CREATE_ALL; op = strtok(NULL, " ,"); } free(tmp); out: sc_log(ctx, "PKCS#11 options: max_virtual_slots=%d slots_per_card=%d " "lock_login=%d atomic=%d pin_unblock_style=%d " "create_slots_flags=0x%X", conf->max_virtual_slots, conf->slots_per_card, conf->lock_login, conf->atomic, conf->pin_unblock_style, conf->create_slots_flags); } OpenSC-0.26.1/src/pkcs11/onepin-opensc-pkcs11.dll.manifest000066400000000000000000000010041474147347300230100ustar00rootroot00000000000000 OpenSC-0.26.1/src/pkcs11/opensc-pkcs11.dll.manifest000066400000000000000000000010041474147347300215220ustar00rootroot00000000000000 OpenSC-0.26.1/src/pkcs11/opensc-pkcs11.pc.in000066400000000000000000000003141474147347300201540ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: OpenSC smartcard framework Description: OpenSC PKCS#11 module Version: @VERSION@ Libs: -L${libdir} -lopensc-pkcs11 OpenSC-0.26.1/src/pkcs11/openssl.c000066400000000000000000000671041474147347300164750ustar00rootroot00000000000000/* * OpenSSL helper functions, e.g. for implementing MD5 support * et al * * 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., 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 #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 */ #ifndef OPENSSL_NO_ENGINE #include #endif /* OPENSSL_NO_ENGINE */ #include #include #include "sc-pkcs11.h" static CK_RV sc_pkcs11_openssl_md_init(sc_pkcs11_operation_t *); static CK_RV sc_pkcs11_openssl_md_update(sc_pkcs11_operation_t *, CK_BYTE_PTR, CK_ULONG); 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 }, { -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 */ }; 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 */ }; static sc_pkcs11_mechanism_type_t openssl_sha256_mech = { CKM_SHA256, { 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 */ }; static sc_pkcs11_mechanism_type_t openssl_sha384_mech = { CKM_SHA384, { 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 */ }; static sc_pkcs11_mechanism_type_t openssl_sha512_mech = { CKM_SHA512, { 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 */ }; static sc_pkcs11_mechanism_type_t openssl_gostr3411_mech = { CKM_GOSTR3411, { 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 */ }; static sc_pkcs11_mechanism_type_t openssl_md5_mech = { CKM_MD5, { 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 */ }; static sc_pkcs11_mechanism_type_t openssl_ripemd160_mech = { CKM_RIPEMD160, { 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 */ }; // clang-format on static void * dup_mem(void *in, size_t in_len) { void *out = malloc(in_len); if (out) memcpy(out, in, 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) { 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 void (*locking_cb)(int, int, const char *, int); locking_cb = CRYPTO_get_locking_callback(); if (locking_cb) CRYPTO_set_locking_callback(NULL); #endif e = ENGINE_by_id("gost"); if (!e) { #if !defined(OPENSSL_NO_STATIC_ENGINE) && !defined(OPENSSL_NO_GOST) && !defined(LIBRESSL_VERSION_NUMBER) /* ENGINE_load_gost removed in 1.1 */ #if OPENSSL_VERSION_NUMBER < 0x10100000L ENGINE_load_gost(); #endif e = ENGINE_by_id("gost"); #else /* try to load dynamic gost engine */ e = ENGINE_by_id("dynamic"); if (!e) { ENGINE_load_dynamic(); e = ENGINE_by_id("dynamic"); } if (e && (!ENGINE_ctrl_cmd_string(e, "SO_PATH", "gost", 0) || !ENGINE_ctrl_cmd_string(e, "LOAD", NULL, 0))) { ENGINE_free(e); e = NULL; } #endif /* !OPENSSL_NO_STATIC_ENGINE && !OPENSSL_NO_GOST && !LIBRESSL_VERSION_NUMBER */ } if (e) { ENGINE_set_default(e, ENGINE_METHOD_ALL); ENGINE_free(e); } #if OPENSSL_VERSION_NUMBER < 0x10100000L if (locking_cb) CRYPTO_set_locking_callback(locking_cb); #endif #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); mt = dup_mem(&openssl_gostr3411_mech, sizeof openssl_gostr3411_mech); sc_pkcs11_register_mechanism(p11card, mt, NULL); sc_pkcs11_free_mechanism(&mt); } /* * Handle OpenSSL digest functions */ #define DIGEST_CTX(op) \ (op ? (EVP_MD_CTX *) (op)->priv_data : NULL) static CK_RV sc_pkcs11_openssl_md_init(sc_pkcs11_operation_t *op) { sc_pkcs11_mechanism_type_t *mt; EVP_MD_CTX *md_ctx; EVP_MD *md; if (!op || !(mt = op->type) || !(md = (EVP_MD *) mt->mech_data)) return CKR_ARGUMENTS_BAD; if (!(md_ctx = EVP_MD_CTX_create())) { sc_log_openssl(context); return CKR_HOST_MEMORY; } 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; } static CK_RV sc_pkcs11_openssl_md_update(sc_pkcs11_operation_t *op, CK_BYTE_PTR pData, CK_ULONG 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; } static CK_RV sc_pkcs11_openssl_md_final(sc_pkcs11_operation_t *op, CK_BYTE_PTR pDigest, CK_ULONG_PTR pulDigestLen) { 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; } 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) { if (op) { EVP_MD_CTX *md_ctx = DIGEST_CTX(op); if (md_ctx) EVP_MD_CTX_destroy(md_ctx); op->priv_data = NULL; } } #if !defined(OPENSSL_NO_EC) static void reverse(unsigned char *buf, size_t len) { unsigned char tmp; size_t i; for (i = 0; i < len / 2; ++i) { tmp = buf[i]; buf[i] = buf[len - 1 - i]; buf[len - 1 - i] = tmp; } } 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 = 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) { 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; } /* FIXME: fully check params[] */ if (params_len > 0 && params[params_len - 1] >= 1 && params[params_len - 1] <= 3) { paramset[0] += params[params_len - 1] - 1; r = EVP_PKEY_CTX_ctrl_str(pkey_ctx, "paramset", paramset); } else r = -1; if (r == 1) 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 && 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); ASN1_OCTET_STRING_free(octet); P = EC_POINT_new(group); if (P && X && Y) 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); } } EVP_PKEY_CTX_free(pkey_ctx); EVP_PKEY_free(pkey); if (r != 1) { return CKR_GENERAL_ERROR; } return ret_vrf == 1 ? CKR_OK : CKR_SIGNATURE_INVALID; } #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 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->mechanism == CKM_GOSTR3410) { #if !defined(OPENSSL_NO_EC) return gostr3410_verify_data(pubkey, pubkey_len, pubkey_params, pubkey_params_len, data, data_len, signat, signat_len); #else (void)pubkey_params, (void)pubkey_params_len; /* no warning */ return CKR_FUNCTION_NOT_SUPPORTED; #endif } /* * 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. */ pubkey_tmp = pubkey; /* pass in so pubkey pointer is not modified */ pkey = d2i_PUBKEY(NULL, &pubkey_tmp, pubkey_len); if (pkey == NULL) { sc_log_openssl(context); return CKR_GENERAL_ERROR; } 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); /* 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) { sc_log_openssl(context); sc_log(context, "EVP_VerifyFinal(): Signature invalid"); return CKR_SIGNATURE_INVALID; } else { sc_log_openssl(context); sc_log(context, "EVP_VerifyFinal() returned %d\n", res); return CKR_GENERAL_ERROR; } } 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; 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; } 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; 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); EVP_PKEY_CTX_free(ctx); return CKR_ARGUMENTS_BAD; } 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_outlen = EVP_PKEY_size(pkey); rsa_out = calloc(1, rsa_outlen); if (rsa_out == NULL) { EVP_PKEY_free(pkey); EVP_PKEY_CTX_free(ctx); return CKR_DEVICE_MEMORY; } if (EVP_PKEY_verify_recover(ctx, rsa_out, &rsa_outlen, signat, signat_len) != 1) { sc_log_openssl(context); free(rsa_out); 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 ((unsigned int) rsa_outlen == data_len && memcmp(rsa_out, data, data_len) == 0) rv = CKR_OK; else rv = CKR_SIGNATURE_INVALID; free(rsa_out); } return rv; } #endif OpenSC-0.26.1/src/pkcs11/pkcs11-display.c000066400000000000000000001627031474147347300175600ustar00rootroot00000000000000/* * Copyright (C) 2015 Mathias Brossard * * 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" #if !defined(_MSC_VER) || _MSC_VER >= 1800 #include #endif #include #ifdef ENABLE_OPENSSL #include #endif #include "pkcs11-display.h" /* Some Netscape/Mozilla-specific stuff: * http://www.opensource.apple.com/darwinsource/10.3/SecurityNssAsn1-11/nssDER/Source/pkcs11n.h */ /* * Netscape-defined object classes * */ #define CKO_NETSCAPE 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) /* * Netscape-defined object attributes * */ #define CKA_NETSCAPE 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_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_TRUST (CKA_NETSCAPE + 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) /* "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) static char * buf_spec(CK_VOID_PTR buf_addr, CK_ULONG buf_len) { static char ret[64]; #if !defined(_MSC_VER) || _MSC_VER >= 1800 const size_t prwidth = sizeof(CK_VOID_PTR) * 2; sprintf(ret, "%0*"PRIxPTR" / %ld", (int) prwidth, (uintptr_t) buf_addr, buf_len); #else if (sizeof(CK_VOID_PTR) == 4) sprintf(ret, "%08lx / %lu", (unsigned long) buf_addr, buf_len); else sprintf(ret, "%016llx / %lu", (unsigned long long) buf_addr, buf_len); #endif return ret; } void print_enum(FILE *f, CK_LONG type, CK_VOID_PTR value, CK_ULONG size, CK_VOID_PTR arg) { enum_spec *spec = (enum_spec*)arg; CK_ULONG i; CK_ULONG ctype = *((CK_ULONG_PTR)value); for(i = 0; i < spec->size; i++) { if(spec->specs[i].type == ctype) { fprintf(f, "%s\n", spec->specs[i].name); return; } } 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) { 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) { CK_ULONG i; if((CK_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; memset(ascii, ' ', sizeof ascii); ascii[sizeof ascii -1] = 0; fprintf(f, "%s", buf_spec(value, size)); for(i = 0; i < size; i++) { CK_BYTE val; if (i && (i % 16) == 0) { fprintf(f, "\n %08X %s %s", offset, hex, ascii); offset += 16; hex_ptr = hex; ascii_ptr = ascii; memset(ascii, ' ', sizeof ascii -1); } val = ((CK_BYTE *)value)[i]; /* hex */ sprintf(hex_ptr, "%02X ", val); hex_ptr += 3; /* ascii */ if (val > 31 && val < 128) *ascii_ptr = val; else *ascii_ptr = '.'; ascii_ptr++; } /* padding */ while (strlen(hex) < 3*16) strcat(hex, " "); fprintf(f, "\n %08X %s %s", offset, hex, ascii); } else { if (value != NULL) fprintf(f, "EMPTY"); else fprintf(f, "NULL [size : 0x%lX (%ld)]", size, size); } 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_generic(f, type, value, size, arg); if(size && value) { X509_NAME *name; const unsigned char *tmp = value; name = d2i_X509_NAME(NULL, &tmp, size); if(name) { BIO *bio = BIO_new(BIO_s_file()); BIO_set_fp(bio, f, 0); fprintf(f, " DN: "); X509_NAME_print(bio, name, XN_FLAG_RFC2253); fprintf(f, "\n"); BIO_free(bio); } } } #endif void print_print(FILE *f, CK_LONG type, CK_VOID_PTR value, CK_ULONG size, CK_VOID_PTR arg) { CK_ULONG i, j=0; CK_BYTE c; if((CK_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++) { if (((j % 4) == 0) && (j != 0)) fprintf(f, " "); c = ((CK_BYTE *)value)[i+j]; fprintf(f, "%02X", c); } fprintf(f, "\n "); for(j = 0; ((i + j < size) && (j < 32)); j++) { if (((j % 4) == 0) && (j != 0)) fprintf(f, " "); c = ((CK_BYTE *)value)[i + j]; if((c > 32) && (c < 128)) fprintf(f, " %c", c); else fprintf(f, " ."); } } if(j == 32) fprintf(f, "\n "); } else { fprintf(f, "EMPTY"); } 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_PROFILE , "CKO_PROFILE " }, { 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 " } }; 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_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" }, }; static enum_specs ck_key_s[] = { { CKK_RSA , "CKK_RSA " }, { CKK_DSA , "CKK_DSA " }, { CKK_DH , "CKK_DH " }, { CKK_EC , "CKK_EC " }, { CKK_EC_EDWARDS , "CKK_EC_EDWARDS " }, { CKK_EC_MONTGOMERY , "CKK_EC_MONTOGMERY " }, { 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 " } }; 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_SHA224_RSA_PKCS , "CKM_SHA224_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_SHA3_224_RSA_PKCS , "CKM_SHA3_224_RSA_PKCS " }, { 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_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_SHA224_RSA_PKCS_PSS , "CKM_SHA224_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_SHA3_224_RSA_PKCS_PSS , "CKM_SHA3_224_RSA_PKCS_PSS " }, { 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_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_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_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_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_SHA224 , "CKM_SHA224 " }, { CKM_SHA224_HMAC , "CKM_SHA224_HMAC " }, { CKM_SHA224_HMAC_GENERAL , "CKM_SHA224_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_ECDSA_SHA224 , "CKM_ECDSA_SHA224 " }, { CKM_ECDSA_SHA256 , "CKM_ECDSA_SHA256 " }, { CKM_ECDSA_SHA384 , "CKM_ECDSA_SHA384 " }, { CKM_ECDSA_SHA512 , "CKM_ECDSA_SHA512 " }, { 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_ECDH1_DERIVE , "CKM_ECDH1_DERIVE " }, { CKM_ECDH1_COFACTOR_DERIVE , "CKM_ECDH1_COFACTOR_DERIVE " }, { CKM_ECMQV_DERIVE , "CKM_ECMQV_DERIVE " }, { CKM_EDDSA , "CKM_EDDSA " }, { CKM_XEDDSA , "CKM_XEDDSA " }, { 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_CMAC , "CKM_AES_CMAC " }, { CKM_AES_CTS , "CKM_AES_CTS " }, { 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_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_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_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_AES_KEY_WRAP , "CKM_AES_KEY_WRAP " }, { 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_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" } }; static enum_specs ck_usr_s[] = { { 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" } }; static enum_specs ck_ckd_s[] = { { CKD_NULL, "CKD_NULL" }, { CKD_SHA1_KDF, "CKD_SHA1_KDF" }, { CKD_SHA224_KDF, "CKD_SHA224_KDF" }, { CKD_SHA256_KDF, "CKD_SHA256_KDF" }, { CKD_SHA384_KDF, "CKD_SHA384_KDF" }, { CKD_SHA512_KDF, "CKD_SHA512_KDF" }, }; #define SZ_SPECS sizeof(enum_specs) enum_spec ck_types[] = { { 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"}, { 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" }, }; 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 }, { CKA_TOKEN , "CKA_TOKEN ", print_boolean, NULL }, { CKA_PRIVATE , "CKA_PRIVATE ", print_boolean, NULL }, { CKA_LABEL , "CKA_LABEL ", print_print, NULL }, { CKA_APPLICATION , "CKA_APPLICATION ", print_print, NULL }, { CKA_VALUE , "CKA_VALUE ", print_generic, NULL }, { CKA_OBJECT_ID , "CKA_OBJECT_ID ", print_generic, NULL }, { CKA_CERTIFICATE_TYPE , "CKA_CERTIFICATE_TYPE ", print_enum, ck_crt_t }, #ifdef ENABLE_OPENSSL { CKA_ISSUER , "CKA_ISSUER ", print_dn, NULL }, #else { CKA_ISSUER , "CKA_ISSUER ", print_generic, NULL }, #endif { CKA_SERIAL_NUMBER , "CKA_SERIAL_NUMBER ", print_generic, NULL }, #ifdef ENABLE_OPENSSL { CKA_AC_ISSUER , "CKA_AC_ISSUER ", print_dn, NULL }, #else { CKA_AC_ISSUER , "CKA_AC_ISSUER ", print_generic, NULL }, #endif { CKA_OWNER , "CKA_OWNER ", print_generic, NULL }, { CKA_ATTR_TYPES , "CKA_ATTR_TYPES ", print_generic, NULL }, { CKA_TRUSTED , "CKA_TRUSTED ", print_generic, NULL }, { CKA_CERTIFICATE_CATEGORY, "CKA_CERTIFICATE_CATEGORY ", print_generic, NULL }, { CKA_JAVA_MIDP_SECURITY_DOMAIN, "CKA_JAVA_MIDP_SECURITY_DOMAIN ", print_generic, NULL }, { CKA_URL , "CKA_URL ", print_generic, NULL }, { CKA_HASH_OF_SUBJECT_PUBLIC_KEY, "CKA_HASH_OF_SUBJECT_PUBLIC_KEY ", print_generic, NULL }, { CKA_HASH_OF_ISSUER_PUBLIC_KEY, "CKA_HASH_OF_ISSUER_PUBLIC_KEY ", print_generic, NULL }, { CKA_CHECK_VALUE , "CKA_CHECK_VALUE ", print_generic, NULL }, { CKA_KEY_TYPE , "CKA_KEY_TYPE ", print_enum, ck_key_t }, #ifdef ENABLE_OPENSSL { CKA_SUBJECT , "CKA_SUBJECT ", print_dn, NULL }, #else { 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 }, { CKA_WRAP , "CKA_WRAP ", print_boolean, NULL }, { CKA_UNWRAP , "CKA_UNWRAP ", print_boolean, NULL }, { CKA_SIGN , "CKA_SIGN ", print_boolean, NULL }, { CKA_SIGN_RECOVER , "CKA_SIGN_RECOVER ", print_boolean, NULL }, { CKA_VERIFY , "CKA_VERIFY ", print_boolean, NULL }, { CKA_VERIFY_RECOVER , "CKA_VERIFY_RECOVER ", print_boolean, NULL }, { CKA_DERIVE , "CKA_DERIVE ", print_boolean, NULL }, { CKA_START_DATE , "CKA_START_DATE ", print_generic, NULL }, { CKA_END_DATE , "CKA_END_DATE ", print_generic, NULL }, { CKA_MODULUS , "CKA_MODULUS ", print_generic, NULL }, { CKA_MODULUS_BITS , "CKA_MODULUS_BITS ", print_generic, NULL }, { CKA_PUBLIC_EXPONENT , "CKA_PUBLIC_EXPONENT ", print_generic, NULL }, { CKA_PRIVATE_EXPONENT , "CKA_PRIVATE_EXPONENT ", print_generic, NULL }, { CKA_PRIME_1 , "CKA_PRIME_1 ", print_generic, NULL }, { CKA_PRIME_2 , "CKA_PRIME_2 ", print_generic, NULL }, { 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 }, { CKA_PRIME_BITS , "CKA_PRIME_BITS ", print_generic, NULL }, { CKA_SUB_PRIME_BITS , "CKA_SUB_PRIME_BITS ", print_generic, NULL }, { CKA_VALUE_BITS , "CKA_VALUE_BITS ", print_generic, NULL }, { CKA_VALUE_LEN , "CKA_VALUE_LEN ", print_generic, NULL }, { CKA_EXTRACTABLE , "CKA_EXTRACTABLE ", print_boolean, NULL }, { CKA_LOCAL , "CKA_LOCAL ", print_boolean, NULL }, { CKA_NEVER_EXTRACTABLE , "CKA_NEVER_EXTRACTABLE", print_boolean, NULL }, { 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_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 }, { CKA_HW_FEATURE_TYPE , "CKA_HW_FEATURE_TYPE ", print_generic, NULL }, { CKA_RESET_ON_INIT , "CKA_RESET_ON_INIT ", print_generic, NULL }, { CKA_HAS_RESET , "CKA_HAS_RESET ", print_generic, NULL }, { CKA_PIXEL_X , "CKA_PIXEL_X ", print_generic, NULL }, { CKA_PIXEL_Y , "CKA_PIXEL_Y ", print_generic, NULL }, { CKA_RESOLUTION , "CKA_RESOLUTION ", print_generic, NULL }, { CKA_CHAR_ROWS , "CKA_CHAR_ROWS ", print_generic, NULL }, { CKA_CHAR_COLUMNS , "CKA_CHAR_COLUMNS ", print_generic, NULL }, { CKA_COLOR , "CKA_COLOR ", print_generic, NULL }, { CKA_BITS_PER_PIXEL , "CKA_BITS_PER_PIXEL ", print_generic, NULL }, { CKA_CHAR_SETS , "CKA_CHAR_SETS ", print_generic, NULL }, { 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 }, { 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 }, }; // clang-format on CK_ULONG ck_attribute_num = sizeof(ck_attribute_specs)/sizeof(type_spec); const char * lookup_enum_spec(enum_spec *spec, CK_ULONG value) { CK_ULONG i; for(i = 0; i < spec->size; i++) if(spec->specs[i].type == value) return spec->specs[i].name; return NULL; } const char * lookup_enum(CK_ULONG type, CK_ULONG value) { CK_ULONG i; for(i = 0; ck_types[i].type < ( sizeof(ck_types) / sizeof(enum_spec) ) ; i++) if(ck_types[i].type == type) return lookup_enum_spec(&(ck_types[i]), value); return NULL; } 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, "\n"); } void print_ck_info(FILE *f, CK_INFO *info) { fprintf(f, " cryptokiVersion: %d.%d\n", info->cryptokiVersion.major, info->cryptokiVersion.minor ); fprintf(f, " manufacturerID: '%32.32s'\n", info->manufacturerID ); fprintf(f, " flags: %0lx\n", info->flags ); fprintf(f, " libraryDescription: '%32.32s'\n", info->libraryDescription ); fprintf(f, " libraryVersion: %d.%d\n", info->libraryVersion.major, info->libraryVersion.minor ); } void print_slot_list(FILE *f, CK_SLOT_ID_PTR pSlotList, CK_ULONG ulCount) { CK_ULONG i; if(pSlotList) { for (i = 0; i < ulCount; i++) fprintf(f, "Slot %ld\n", pSlotList[i]); } else { fprintf(f, "Count is %ld\n", ulCount); } } void print_slot_info(FILE *f, CK_SLOT_INFO *info) { size_t i; enum_specs ck_flags[] = { { CKF_TOKEN_PRESENT , "CKF_TOKEN_PRESENT " }, { CKF_REMOVABLE_DEVICE , "CKF_REMOVABLE_DEVICE " }, { CKF_HW_SLOT , "CKF_HW_SLOT " }, }; fprintf(f, " slotDescription: '%32.32s'\n", info->slotDescription ); fprintf(f, " '%32.32s'\n", info->slotDescription+32 ); fprintf(f, " manufacturerID: '%32.32s'\n", info->manufacturerID ); fprintf(f, " hardwareVersion: %d.%d\n", info->hardwareVersion.major, info->hardwareVersion.minor ); fprintf(f, " firmwareVersion: %d.%d\n", info->firmwareVersion.major, info->firmwareVersion.minor ); fprintf(f, " flags: %0lx\n", info->flags ); for(i = 0; i < sizeof (ck_flags) / sizeof (*ck_flags); i++) if(info->flags & ck_flags[i].type) fprintf(f, " %s\n", ck_flags[i].name); } void print_token_info(FILE *f, CK_TOKEN_INFO *info) { size_t i; enum_specs ck_flags[] = { { CKF_RNG , "CKF_RNG " }, { CKF_WRITE_PROTECTED , "CKF_WRITE_PROTECTED " }, { CKF_LOGIN_REQUIRED , "CKF_LOGIN_REQUIRED " }, { CKF_USER_PIN_INITIALIZED , "CKF_USER_PIN_INITIALIZED " }, { CKF_RESTORE_KEY_NOT_NEEDED , "CKF_RESTORE_KEY_NOT_NEEDED " }, { CKF_CLOCK_ON_TOKEN , "CKF_CLOCK_ON_TOKEN " }, { CKF_PROTECTED_AUTHENTICATION_PATH, "CKF_PROTECTED_AUTHENTICATION_PATH" }, { CKF_DUAL_CRYPTO_OPERATIONS , "CKF_DUAL_CRYPTO_OPERATIONS " }, { CKF_TOKEN_INITIALIZED , "CKF_TOKEN_INITIALIZED " }, { CKF_SECONDARY_AUTHENTICATION , "CKF_SECONDARY_AUTHENTICATION " }, { CKF_USER_PIN_COUNT_LOW , "CKF_USER_PIN_COUNT_LOW " }, { CKF_USER_PIN_FINAL_TRY , "CKF_USER_PIN_FINAL_TRY " }, { CKF_USER_PIN_LOCKED , "CKF_USER_PIN_LOCKED " }, { CKF_USER_PIN_TO_BE_CHANGED , "CKF_USER_PIN_TO_BE_CHANGED " }, { 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 " } }; fprintf(f, " label: '%32.32s'\n", info->label ); fprintf(f, " manufacturerID: '%32.32s'\n", info->manufacturerID ); fprintf(f, " model: '%16.16s'\n", info->model ); fprintf(f, " serialNumber: '%16.16s'\n", info->serialNumber ); fprintf(f, " ulMaxSessionCount: %ld\n", info->ulMaxSessionCount ); fprintf(f, " ulSessionCount: %ld\n", info->ulSessionCount ); fprintf(f, " ulMaxRwSessionCount: %ld\n", info->ulMaxRwSessionCount ); fprintf(f, " ulRwSessionCount: %ld\n", info->ulRwSessionCount ); fprintf(f, " ulMaxPinLen: %ld\n", info->ulMaxPinLen ); fprintf(f, " ulMinPinLen: %ld\n", info->ulMinPinLen ); fprintf(f, " ulTotalPublicMemory: %ld\n", info->ulTotalPublicMemory ); fprintf(f, " ulFreePublicMemory: %ld\n", info->ulFreePublicMemory ); fprintf(f, " ulTotalPrivateMemory: %ld\n", info->ulTotalPrivateMemory ); fprintf(f, " ulFreePrivateMemory: %ld\n", info->ulFreePrivateMemory ); fprintf(f, " hardwareVersion: %d.%d\n", info->hardwareVersion.major, info->hardwareVersion.minor ); fprintf(f, " firmwareVersion: %d.%d\n", info->firmwareVersion.major, info->firmwareVersion.minor ); fprintf(f, " time: '%16.16s'\n", info->utcTime ); fprintf(f, " flags: %0lx\n", info->flags ); for(i = 0; i < sizeof (ck_flags) / sizeof (*ck_flags); i++) if(info->flags & ck_flags[i].type) fprintf(f, " %s\n", ck_flags[i].name); } void print_mech_list(FILE *f, CK_MECHANISM_TYPE_PTR pMechanismList, CK_ULONG ulMechCount) { CK_ULONG imech; if(pMechanismList) { for (imech = 0; imech < ulMechCount; imech++) { const char *name = lookup_enum(MEC_T, pMechanismList[imech]); if (name) fprintf(f, "%30s \n", name); else fprintf(f, " Unknown Mechanism (%08lx) \n", pMechanismList[imech]); } } else { fprintf(f, "Count is %ld\n", ulMechCount); } } 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; if (name) fprintf(f, "%s : ", name); else fprintf(f, "Unknown Mechanism (%08lx) : ", type); 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 " : ""); } void print_attribute_list(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++) { 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); } else { fprintf(f, "%s\n", buf_spec(pTemplate[j].pValue, pTemplate[j].ulValueLen)); } k = ck_attribute_num; } } if (!found) { fprintf(f, " CKA_? (0x%08lx) ", pTemplate[j].type); fprintf(f, "%s\n", buf_spec(pTemplate[j].pValue, pTemplate[j].ulValueLen)); } } } void 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++) { if(ck_attribute_specs[k].type == pTemplate[j].type) { found = 1; fprintf(f, " %s ", ck_attribute_specs[k].name); fprintf(f, "%s\n", buf_spec(pTemplate[j].pValue, pTemplate[j].ulValueLen)); k = ck_attribute_num; } } if (!found) { fprintf(f, " CKA_? (0x%08lx) ", pTemplate[j].type); fprintf(f, "%s\n", buf_spec(pTemplate[j].pValue, pTemplate[j].ulValueLen)); } } } void 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 " } }; fprintf(f, " slotID: %ld\n", info->slotID ); 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++) { if(info->flags & ck_flags[i].type) fprintf(f, " %s\n", ck_flags[i].name); } 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); } } OpenSC-0.26.1/src/pkcs11/pkcs11-display.h000066400000000000000000000055441474147347300175640ustar00rootroot00000000000000#ifndef PKCS11_DISPLAY_H #define PKCS11_DISPLAY_H /* * Copyright (C) 2015 Mathias Brossard * * 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 "pkcs11.h" #ifdef __cplusplus extern "C" { #endif typedef void (display_func) \ (FILE *, CK_LONG, CK_VOID_PTR, CK_ULONG, CK_VOID_PTR); typedef struct { CK_ULONG type; const char *name; } enum_specs; typedef struct { CK_ULONG type; enum_specs *specs; CK_ULONG size; const char *name; } enum_spec; typedef struct { CK_ULONG type; const char * name; display_func* display; void * arg; } type_spec; enum ck_type{ OBJ_T, PROFILE_T, KEY_T, CRT_T, MEC_T, MGF_T, USR_T, STA_T, CKD_T, RV_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 show_error (FILE *f, char *str, CK_RV rc); void print_ck_info(FILE *f, CK_INFO *info); void print_slot_list(FILE *f, CK_SLOT_ID_PTR pSlotList, CK_ULONG ulCount); void print_slot_info(FILE *f, CK_SLOT_INFO *info); void print_token_info(FILE *f, CK_TOKEN_INFO *info); void print_mech_list(FILE *f, CK_MECHANISM_TYPE_PTR pMechanismList, CK_ULONG ulMechCount); void print_mech_info(FILE *f, CK_MECHANISM_TYPE type, CK_MECHANISM_INFO_PTR minfo); void print_attribute_list(FILE *f, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount); 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; extern enum_spec ck_types[]; #ifdef __cplusplus }; #endif #endif OpenSC-0.26.1/src/pkcs11/pkcs11-global.c000066400000000000000000000663241474147347300173550ustar00rootroot00000000000000/* * pkcs11-global.c: PKCS#11 module level functions and function table * * Copyright (C) 2002 Timo Teräs * * 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 #ifdef HAVE_SYS_TIME_H #include #endif #ifdef PKCS11_THREAD_LOCKING #if defined(HAVE_PTHREAD) #include #elif defined(_WIN32) #include #endif #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" #endif sc_context_t *context = NULL; struct sc_pkcs11_config sc_pkcs11_conf; list_t sessions; list_t virtual_slots; #if !defined(_WIN32) 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; 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;; pthread_mutex_init(m, NULL); *mutex = m; return CKR_OK; } CK_RV mutex_lock(void *p) { if (pthread_mutex_lock((pthread_mutex_t *) p) == 0) return CKR_OK; else return CKR_GENERAL_ERROR; } CK_RV mutex_unlock(void *p) { if (pthread_mutex_unlock((pthread_mutex_t *) p) == 0) return CKR_OK; else return CKR_GENERAL_ERROR; } CK_RV mutex_destroy(void *p) { pthread_mutex_destroy((pthread_mutex_t *) p); free(p); return CKR_OK; } static CK_C_INITIALIZE_ARGS _def_locks = { mutex_create, mutex_destroy, mutex_lock, mutex_unlock, 0, NULL }; #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) { CRITICAL_SECTION *m; m = calloc(1, sizeof(*m)); if (m == NULL) return CKR_GENERAL_ERROR; InitializeCriticalSection(m); *mutex = m; return CKR_OK; } CK_RV mutex_lock(void *p) { EnterCriticalSection((CRITICAL_SECTION *) p); return CKR_OK; } CK_RV mutex_unlock(void *p) { LeaveCriticalSection((CRITICAL_SECTION *) p); return CKR_OK; } CK_RV mutex_destroy(void *p) { DeleteCriticalSection((CRITICAL_SECTION *) p); free(p); return CKR_OK; } static CK_C_INITIALIZE_ARGS _def_locks = { mutex_create, mutex_destroy, mutex_lock, mutex_unlock, 0, NULL }; #define HAVE_OS_LOCKING #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; #else static CK_C_INITIALIZE_ARGS_PTR default_mutex_funcs = NULL; #endif /* wrapper for the locking functions for libopensc */ static int sc_create_mutex(void **m) { if (global_locking == NULL) return SC_SUCCESS; if (global_locking->CreateMutex(m) == CKR_OK) return SC_SUCCESS; else return SC_ERROR_INTERNAL; } static int sc_lock_mutex(void *m) { if (global_locking == NULL) return SC_SUCCESS; if (global_locking->LockMutex(m) == CKR_OK) return SC_SUCCESS; else return SC_ERROR_INTERNAL; } static int sc_unlock_mutex(void *m) { if (global_locking == NULL) return SC_SUCCESS; if (global_locking->UnlockMutex(m) == CKR_OK) return SC_SUCCESS; else return SC_ERROR_INTERNAL; } static int sc_destroy_mutex(void *m) { if (global_locking == NULL) return SC_SUCCESS; if (global_locking->DestroyMutex(m) == CKR_OK) return SC_SUCCESS; else return SC_ERROR_INTERNAL; } static sc_thread_context_t sc_thread_ctx = { 0, sc_create_mutex, sc_lock_mutex, sc_unlock_mutex, sc_destroy_mutex, NULL }; /* simclist helpers to locate interesting objects by ID */ static int session_list_seeker(const void *el, const void *key) { const struct sc_pkcs11_session *session = (struct sc_pkcs11_session *)el; if ((el == NULL) || (key == NULL)) return 0; if (session->handle == *(CK_SESSION_HANDLE*)key) return 1; return 0; } static int slot_list_seeker(const void *el, const void *key) { const struct sc_pkcs11_slot *slot = (struct sc_pkcs11_slot *)el; if ((el == NULL) || (key == NULL)) return 0; if (slot->id == *(CK_SLOT_ID *)key) return 1; 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; #endif int rc; 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 && 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) { 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; } rv = sc_pkcs11_init_lock((CK_C_INITIALIZE_ARGS_PTR) pInitArgs); if (rv != CKR_OK) goto out; /* set context options */ memset(&ctx_opts, 0, sizeof(sc_context_param_t)); ctx_opts.ver = 0; ctx_opts.app_name = MODULE_APP_NAME; ctx_opts.thread_ctx = &sc_thread_ctx; rc = sc_context_create(&context, &ctx_opts); if (rc != SC_SUCCESS) { rv = CKR_GENERAL_ERROR; goto out; } /* Load configuration */ load_pkcs11_parameters(&sc_pkcs11_conf, context); /* List of sessions */ if (0 != list_init(&sessions)) { rv = CKR_HOST_MEMORY; goto out; } list_attributes_seeker(&sessions, session_list_seeker); /* List of slots */ if (0 != list_init(&virtual_slots)) { rv = CKR_HOST_MEMORY; goto out; } list_attributes_seeker(&virtual_slots, slot_list_seeker); card_detect_all(); out: if (context != NULL) SC_LOG_RV("C_Initialize() = %s", rv); if (rv != CKR_OK) { if (context != NULL) { sc_release_context(context); context = NULL; } /* Release and destroy the mutex */ sc_pkcs11_free_lock(); } /* protect from multiple threads tryng to setup locking */ nesting--; C_INITIALIZE_M_UNLOCK return rv; } CK_RV C_Finalize(CK_VOID_PTR pReserved) { int i; void *p; sc_pkcs11_slot_t *slot; CK_RV rv; if (pReserved != NULL_PTR) return CKR_ARGUMENTS_BAD; #if !defined(_WIN32) sc_notify_close(); #endif if (context == NULL) return CKR_CRYPTOKI_NOT_INITIALIZED; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; sc_log(context, "C_Finalize()"); /* cancel pending calls */ in_finalize = 1; sc_cancel(context); /* remove all cards from readers */ for (i=0; i < (int)sc_ctx_get_reader_count(context); i++) card_removed(sc_ctx_get_reader(context, i)); while ((p = list_fetch(&sessions))) free(p); list_destroy(&sessions); while ((slot = list_fetch(&virtual_slots))) { list_destroy(&slot->objects); list_destroy(&slot->logins); free(slot); } list_destroy(&virtual_slots); sc_release_context(context); context = NULL; /* Release and destroy the mutex */ sc_pkcs11_free_lock(); return rv; } CK_RV get_info_version(CK_INFO_PTR pInfo, CK_VERSION version) { CK_RV rv = CKR_OK; if (pInfo == NULL_PTR) return CKR_ARGUMENTS_BAD; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; sc_log(context, "C_GetInfo()"); memset(pInfo, 0, sizeof(CK_INFO)); pInfo->cryptokiVersion.major = version.major; pInfo->cryptokiVersion.minor = version.minor; strcpy_bp(pInfo->manufacturerID, OPENSC_VS_FF_COMPANY_NAME, sizeof(pInfo->manufacturerID)); strcpy_bp(pInfo->libraryDescription, OPENSC_VS_FF_PRODUCT_NAME, sizeof(pInfo->libraryDescription)); pInfo->libraryVersion.major = OPENSC_VERSION_MAJOR; pInfo->libraryVersion.minor = OPENSC_VERSION_MINOR; sc_pkcs11_unlock(); 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) return CKR_ARGUMENTS_BAD; *ppFunctionList = &pkcs11_function_list; return CKR_OK; } CK_RV C_GetSlotList(CK_BBOOL tokenPresent, /* only slots with token present */ CK_SLOT_ID_PTR pSlotList, /* receives the array of slot IDs */ CK_ULONG_PTR pulCount) /* receives the number of slots */ { CK_SLOT_ID_PTR found = NULL; unsigned int i; CK_ULONG numMatches; sc_pkcs11_slot_t *slot; sc_reader_t *prev_reader = NULL; CK_RV rv; if (pulCount == NULL_PTR) return CKR_ARGUMENTS_BAD; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; 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) { rv = CKR_HOST_MEMORY; goto out; } prev_reader = NULL; numMatches = 0; for (i=0; ireader != 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); *pulCount = numMatches; 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); *pulCount = numMatches; rv = CKR_BUFFER_TOO_SMALL; goto out; } memcpy(pSlotList, found, numMatches * sizeof(CK_SLOT_ID)); *pulCount = numMatches; rv = CKR_OK; sc_log(context, "returned %lu slots\n", numMatches); DEBUG_VSS(NULL, "Returning a new slot list"); out: free (found); sc_pkcs11_unlock(); return rv; } static sc_timestamp_t get_current_time(void) { #if HAVE_GETTIMEOFDAY struct timeval tv; struct timezone tz; sc_timestamp_t curr; if (gettimeofday(&tv, &tz) != 0) return 0; curr = tv.tv_sec; curr *= 1000; curr += tv.tv_usec / 1000; #else struct _timeb time_buf; sc_timestamp_t curr; _ftime(&time_buf); curr = time_buf.time; curr *= 1000; curr += time_buf.millitm; #endif return curr; } CK_RV C_GetSlotInfo(CK_SLOT_ID slotID, CK_SLOT_INFO_PTR pInfo) { struct sc_pkcs11_slot *slot = NULL; sc_timestamp_t now; const char *name; CK_RV rv; if (pInfo == NULL_PTR) return CKR_ARGUMENTS_BAD; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; sc_log(context, "C_GetSlotInfo(0x%lx)", slotID); if (sc_pkcs11_conf.init_sloppy) { /* 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 * it's doing... */ card_detect_all(); } rv = slot_get_slot(slotID, &slot); 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 { now = get_current_time(); if (now >= slot->slot_state_expires || now == 0) { /* Update slot status */ rv = card_detect(slot->reader); sc_log(context, "C_GetSlotInfo() card detect rv 0x%lX", rv); if (rv == CKR_TOKEN_NOT_RECOGNIZED || rv == CKR_OK) slot->slot_info.flags |= CKF_TOKEN_PRESENT; /* Don't ask again within the next second */ slot->slot_state_expires = now + 1000; } } } if (rv == CKR_TOKEN_NOT_PRESENT || rv == CKR_TOKEN_NOT_RECOGNIZED) rv = CKR_OK; if (rv == CKR_OK) memcpy(pInfo, &slot->slot_info, sizeof(CK_SLOT_INFO)); sc_log(context, "C_GetSlotInfo() flags 0x%lX", pInfo->flags); 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; } CK_RV C_GetMechanismList(CK_SLOT_ID slotID, CK_MECHANISM_TYPE_PTR pMechanismList, CK_ULONG_PTR pulCount) { struct sc_pkcs11_slot *slot; CK_RV rv; if (pulCount == NULL_PTR) return CKR_ARGUMENTS_BAD; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; rv = slot_get_token(slotID, &slot); if (rv == CKR_OK) rv = sc_pkcs11_get_mechanism_list(slot->p11card, pMechanismList, pulCount); sc_pkcs11_unlock(); return rv; } CK_RV C_GetMechanismInfo(CK_SLOT_ID slotID, CK_MECHANISM_TYPE type, CK_MECHANISM_INFO_PTR pInfo) { struct sc_pkcs11_slot *slot; CK_RV rv; if (pInfo == NULL_PTR) return CKR_ARGUMENTS_BAD; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; rv = slot_get_token(slotID, &slot); if (rv == CKR_OK) rv = sc_pkcs11_get_mechanism_info(slot->p11card, type, pInfo); sc_pkcs11_unlock(); return rv; } CK_RV C_InitToken(CK_SLOT_ID slotID, CK_CHAR_PTR pPin, CK_ULONG ulPinLen, CK_CHAR_PTR pLabel) { struct sc_pkcs11_session *session; struct sc_pkcs11_slot *slot; unsigned char *label, *cpo; CK_RV rv; unsigned int i; /* 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) { free(label); return rv; } rv = slot_get_token(slotID, &slot); if (rv != CKR_OK) { sc_log(context, "C_InitToken() get token error 0x%lX", rv); goto out; } if (!slot->p11card || !slot->p11card->framework || !slot->p11card->framework->init_token) { sc_log(context, "C_InitToken() not supported by framework"); rv = CKR_FUNCTION_NOT_SUPPORTED; goto out; } /* Make sure there's no open session for this token */ for (i=0; islot == slot) { rv = CKR_SESSION_EXISTS; goto out; } } 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 */ } out: sc_pkcs11_unlock(); sc_log(context, "C_InitToken(pLabel='%s') returns 0x%lX", label, rv); free(label); return rv; } CK_RV C_WaitForSlotEvent(CK_FLAGS flags, /* blocking/nonblocking flag */ CK_SLOT_ID_PTR pSlot, /* location that receives the slot ID */ CK_VOID_PTR pReserved) /* reserved. Should be NULL_PTR */ { sc_reader_t *found; unsigned int mask, events; void *reader_states = NULL; CK_SLOT_ID slot_id; CK_RV rv; int r; if (pReserved != NULL_PTR) 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; mask = SC_EVENT_CARD_EVENTS | SC_EVENT_READER_EVENTS; /* Detect and add new slots for added readers v2.20 */ rv = slot_find_changed(&slot_id, mask); if ((rv == CKR_OK) || (flags & CKF_DONT_BLOCK)) goto out; again: 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); /* Was C_Finalize called ? */ if (in_finalize == 1) return CKR_CRYPTOKI_NOT_INITIALIZED; if ((rv = sc_pkcs11_lock()) != CKR_OK) return rv; if (r != SC_SUCCESS) { sc_log(context, "sc_wait_for_event() returned %d\n", r); rv = sc_to_cryptoki_error(r, "C_WaitForSlotEvent"); goto out; } /* If no changed slot was found (maybe an unsupported card * was inserted/removed) then go waiting again */ rv = slot_find_changed(&slot_id, mask); if (rv != CKR_OK) goto again; out: if (pSlot) *pSlot = slot_id; /* Free allocated readers states holder */ if (reader_states) { sc_log(context, "free reader states"); sc_wait_for_event(context, 0, NULL, NULL, -1, &reader_states); } SC_LOG_RV("C_WaitForSlotEvent() = %s", rv); sc_pkcs11_unlock(); return rv; } /* * Interfaces */ #define NUM_INTERFACES 2 #define DEFAULT_INTERFACE 0 // clang-format off CK_INTERFACE interfaces[NUM_INTERFACES] = { {"PKCS 11", (void *)&pkcs11_function_list_3_0, 0}, {"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, 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 */ CK_RV sc_pkcs11_init_lock(CK_C_INITIALIZE_ARGS_PTR args) { CK_RV rv = CKR_OK; int applock = 0; int oslock = 0; if (global_lock) return CKR_OK; /* No CK_C_INITIALIZE_ARGS pointer, no locking */ if (!args) return CKR_OK; 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. */ global_locking = NULL; if (args->CreateMutex && args->DestroyMutex && args->LockMutex && args->UnlockMutex) { applock = 1; } if ((args->flags & CKF_OS_LOCKING_OK)) { oslock = 1; } /* Based on PKCS#11 v2.11 11.4 */ if (applock && oslock) { /* Shall be used in threaded environment, prefer app provided locking */ 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 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; } if (global_locking != NULL) { /* create mutex */ rv = global_locking->CreateMutex(&global_lock); } return rv; } CK_RV sc_pkcs11_lock(void) { if (context == NULL) return CKR_CRYPTOKI_NOT_INITIALIZED; if (!global_lock) return CKR_OK; if (global_locking) { while (global_locking->LockMutex(global_lock) != CKR_OK) ; } return CKR_OK; } static void __sc_pkcs11_unlock(void *lock) { if (!lock) return; if (global_locking) { while (global_locking->UnlockMutex(lock) != CKR_OK) ; } } void sc_pkcs11_unlock(void) { __sc_pkcs11_unlock(global_lock); } /* * Free the lock - note the lock must be held when * you come here */ void sc_pkcs11_free_lock(void) { void *tempLock; if (!(tempLock = global_lock)) return; /* Clear the global lock pointer - once we've * unlocked the mutex it's as good as gone */ global_lock = NULL; /* Now unlock. On SMP machines the synchronization * primitives should take care of flushing out * all changed data to RAM */ __sc_pkcs11_unlock(tempLock); if (global_locking) global_locking->DestroyMutex(tempLock); global_locking = NULL; } CK_FUNCTION_LIST pkcs11_function_list = { { 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 }; OpenSC-0.26.1/src/pkcs11/pkcs11-object.c000066400000000000000000001501351474147347300173550ustar00rootroot00000000000000/* * pkcs11-object.c: PKCS#11 object management and handling functions * * Copyright (C) 2002 Timo Teräs * * 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 "sc-pkcs11.h" static void sc_find_release(sc_pkcs11_operation_t *operation); /* Pseudo mechanism for the Find operation */ static sc_pkcs11_mechanism_type_t find_mechanism = { 0, /* mech */ {0,0,0}, /* mech_info */ { -1 }, /* key_types */ sizeof(struct sc_pkcs11_find_operation), /* obj_size */ sc_find_release, /* release */ NULL, /* md_init */ NULL, /* md_update */ NULL, /* md_final */ NULL, /* sign_init */ NULL, /* sign_update */ NULL, /* sign_final */ NULL, /* sign_size */ NULL, /* verif_init */ NULL, /* verif_update */ 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 sc_find_release(sc_pkcs11_operation_t *operation) { struct sc_pkcs11_find_operation *fop = (struct sc_pkcs11_find_operation *)operation; if (fop->handles) { free(fop->handles); fop->handles = NULL; } } static CK_RV get_object_from_session(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, struct sc_pkcs11_session **session, struct sc_pkcs11_object **object) { struct sc_pkcs11_session *sess; CK_RV rv; rv = get_session(hSession, &sess); if (rv != CKR_OK) return rv; *object = list_seek(&sess->slot->objects, &hObject); if (!*object) return CKR_OBJECT_HANDLE_INVALID; *session = sess; return CKR_OK; } /* C_CreateObject can be called from C_DeriveKey * which is holding the sc_pkcs11_lock * So dont get the lock again. */ static CK_RV sc_create_object_int(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_ATTRIBUTE_PTR pTemplate, /* the object's template */ CK_ULONG ulCount, /* attributes in template */ CK_OBJECT_HANDLE_PTR phObject, /* receives new object's handle. */ int use_lock) { 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) return CKR_ARGUMENTS_BAD; if (use_lock) { rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; } dump_template(SC_LOG_DEBUG_NORMAL, "C_CreateObject()", pTemplate, ulCount); session = list_seek(&sessions, &hSession); if (!session) { rv = CKR_SESSION_HANDLE_INVALID; 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; else rv = card->framework->create_object(session->slot, pTemplate, ulCount, phObject); out: if (use_lock) sc_pkcs11_unlock(); return rv; } CK_RV C_CreateObject(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_ATTRIBUTE_PTR pTemplate, /* the object's template */ CK_ULONG ulCount, /* attributes in template */ CK_OBJECT_HANDLE_PTR phObject) { return sc_create_object_int(hSession, pTemplate, ulCount, phObject, 1); } CK_RV C_CopyObject(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_OBJECT_HANDLE hObject, /* the object's handle */ CK_ATTRIBUTE_PTR pTemplate, /* template for new object */ CK_ULONG ulCount, /* attributes in template */ CK_OBJECT_HANDLE_PTR phNewObject) /* receives handle of copy */ { return CKR_FUNCTION_NOT_SUPPORTED; } CK_RV C_DestroyObject(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_OBJECT_HANDLE hObject) /* the object's handle */ { CK_RV rv; struct sc_pkcs11_session *session; struct sc_pkcs11_object *object; CK_BBOOL is_token = FALSE; CK_ATTRIBUTE token_attribute = {CKA_TOKEN, &is_token, sizeof(is_token)}; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; sc_log(context, "C_DestroyObject(hSession=0x%lx, hObject=0x%lx)", hSession, hObject); rv = get_object_from_session(hSession, hObject, &session, &object); if (rv != CKR_OK) 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) rv = CKR_FUNCTION_NOT_SUPPORTED; else rv = object->ops->destroy_object(session, object); out: sc_pkcs11_unlock(); return rv; } CK_RV C_GetObjectSize(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_OBJECT_HANDLE hObject, /* the object's handle */ CK_ULONG_PTR pulSize) /* receives size of object */ { return CKR_FUNCTION_NOT_SUPPORTED; } CK_RV C_GetAttributeValue(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_OBJECT_HANDLE hObject, /* the object's handle */ CK_ATTRIBUTE_PTR pTemplate, /* specifies attributes, gets values */ CK_ULONG ulCount) /* attributes in template */ { static CK_RV precedence[] = { CKR_OK, CKR_BUFFER_TOO_SMALL, CKR_ATTRIBUTE_TYPE_INVALID, CKR_ATTRIBUTE_SENSITIVE, -1 }; char object_name[64]; CK_RV j; CK_RV rv; struct sc_pkcs11_session *session; struct sc_pkcs11_object *object; CK_RV res; CK_RV res_type; unsigned int i; const char *name; if (pTemplate == NULL_PTR || ulCount == 0) return CKR_ARGUMENTS_BAD; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; rv = get_object_from_session(hSession, hObject, &session, &object); if (rv != CKR_OK) goto out; /* Debug printf */ snprintf(object_name, sizeof(object_name), "Object %lu", (unsigned long)hObject); res_type = 0; for (i = 0; i < ulCount; i++) { res = object->ops->get_attribute(session, object, &pTemplate[i]); if (res != CKR_OK) pTemplate[i].ulValueLen = (CK_ULONG) - 1; dump_template(SC_LOG_DEBUG_NORMAL, object_name, &pTemplate[i], 1); /* the pkcs11 spec has complicated rules on * what errors take precedence: * CKR_ATTRIBUTE_SENSITIVE * CKR_ATTRIBUTE_INVALID * CKR_BUFFER_TOO_SMALL * It does not exactly specify how other errors * should be handled - we give them highest * precedence */ for (j = 0; precedence[j] != (CK_RV) -1; j++) { if (precedence[j] == res) break; } if (j > res_type) { res_type = j; rv = res; } } 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; } CK_RV C_SetAttributeValue(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_OBJECT_HANDLE hObject, /* the object's handle */ CK_ATTRIBUTE_PTR pTemplate, /* specifies attributes and values */ CK_ULONG ulCount) /* attributes in template */ { CK_RV rv; unsigned int i; struct sc_pkcs11_session *session; struct sc_pkcs11_object *object; if (pTemplate == NULL_PTR || ulCount == 0) return CKR_ARGUMENTS_BAD; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; dump_template(SC_LOG_DEBUG_NORMAL, "C_SetAttributeValue", pTemplate, ulCount); rv = get_object_from_session(hSession, hObject, &session, &object); if (rv != CKR_OK) goto out; if (!(session->flags & CKF_RW_SESSION)) { rv = CKR_SESSION_READ_ONLY; goto out; } if (object->ops->set_attribute == NULL) rv = CKR_FUNCTION_NOT_SUPPORTED; else { for (i = 0; i < ulCount; i++) { rv = object->ops->set_attribute(session, object, &pTemplate[i]); if (rv != CKR_OK) break; } } out: sc_pkcs11_unlock(); return rv; } CK_RV C_FindObjectsInit(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_ATTRIBUTE_PTR pTemplate, /* attribute values to match */ CK_ULONG ulCount) /* attributes in search template */ { CK_RV rv; CK_BBOOL is_private = TRUE; CK_ATTRIBUTE private_attribute = { CKA_PRIVATE, &is_private, sizeof(is_private) }; int match, hide_private; unsigned int i, j; struct sc_pkcs11_session *session; 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; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; rv = get_session(hSession, &session); if (rv != CKR_OK) goto out; sc_log(context, "C_FindObjectsInit(slot = %lu)\n", session->slot->id); dump_template(SC_LOG_DEBUG_NORMAL, "C_FindObjectsInit()", pTemplate, ulCount); rv = session_start_operation(session, SC_PKCS11_OPERATION_FIND, &find_mechanism, &op); operation = (struct sc_pkcs11_find_operation *) op; if (rv != CKR_OK) goto out; operation->current_handle = 0; operation->num_handles = 0; operation->allocated_handles = 0; operation->handles = NULL; slot = session->slot; /* Check whether we should hide private objects */ hide_private = 0; if ((slot->login_user == -1) && (slot->token_info.flags & CKF_LOGIN_REQUIRED)) hide_private = 1; /* For each object in token do */ for (i=0; iobjects); i++) { object = (struct sc_pkcs11_object *)list_get_at(&slot->objects, i); sc_log(context, "Object with handle 0x%lx", object->handle); /* User not logged in and private object? */ if (hide_private) { if (object->ops->get_attribute(session, object, &private_attribute) != CKR_OK) continue; if (is_private) { sc_log(context, "Object %lu/%lu: Private object and not logged in.", slot->id, object->handle); continue; } } /* Try to match every attribute */ match = 1; for (j = 0; j < ulCount; j++) { rv = object->ops->cmp_attribute(session, object, &pTemplate[j]); if (rv == 0) { sc_log(context, "Object %lu/%lu: Attribute 0x%lx does NOT match.", slot->id, object->handle, pTemplate[j].type); match = 0; break; } if (context->debug >= 4) { sc_log(context, "Object %lu/%lu: Attribute 0x%lx matches.", slot->id, object->handle, pTemplate[j].type); } } if (match) { sc_log(context, "Object %lu/%lu matches\n", slot->id, object->handle); /* Realloc handles - remove restriction on only 32 matching objects -dee */ if (operation->num_handles >= operation->allocated_handles) { operation->allocated_handles += SC_PKCS11_FIND_INC_HANDLES; sc_log(context, "realloc for %d handles", operation->allocated_handles); operation->handles = realloc(operation->handles, sizeof(CK_OBJECT_HANDLE) * operation->allocated_handles); if (operation->handles == NULL) { rv = CKR_HOST_MEMORY; goto out; } } operation->handles[operation->num_handles++] = object->handle; } } rv = CKR_OK; sc_log(context, "%d matching objects\n", operation->num_handles); out: sc_pkcs11_unlock(); return rv; } CK_RV C_FindObjects(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_OBJECT_HANDLE_PTR phObject, /* receives object handle array */ CK_ULONG ulMaxObjectCount, /* max handles to be returned */ CK_ULONG_PTR pulObjectCount) /* actual number returned */ { CK_RV rv; 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; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; rv = get_session(hSession, &session); if (rv != CKR_OK) goto out; rv = session_get_operation(session, SC_PKCS11_OPERATION_FIND, &op); operation = (struct sc_pkcs11_find_operation *) op; if (rv != CKR_OK) goto out; to_return = (CK_ULONG) operation->num_handles - operation->current_handle; if (to_return > ulMaxObjectCount) to_return = ulMaxObjectCount; *pulObjectCount = to_return; memcpy(phObject, &operation->handles[operation->current_handle], to_return * sizeof(CK_OBJECT_HANDLE)); operation->current_handle += to_return; out: sc_pkcs11_unlock(); return rv; } CK_RV C_FindObjectsFinal(CK_SESSION_HANDLE hSession) /* the session's handle */ { 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) goto out; rv = session_get_operation(session, SC_PKCS11_OPERATION_FIND, NULL); if (rv == CKR_OK) session_stop_operation(session, SC_PKCS11_OPERATION_FIND); out: sc_pkcs11_unlock(); return rv; } /* * Below here all functions are wrappers to pass all object attribute and method * handling to appropriate object layer. */ CK_RV C_DigestInit(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_MECHANISM_PTR pMechanism) /* the digesting mechanism */ { CK_RV rv; struct sc_pkcs11_session *session; if (pMechanism == NULL_PTR) return CKR_ARGUMENTS_BAD; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; sc_log(context, "C_DigestInit(hSession=0x%lx)", hSession); rv = get_session(hSession, &session); if (rv == CKR_OK) rv = sc_pkcs11_md_init(session, pMechanism); SC_LOG_RV("C_DigestInit() = %s", rv); sc_pkcs11_unlock(); return rv; } CK_RV C_Digest(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_BYTE_PTR pData, /* data to be digested */ CK_ULONG ulDataLen, /* bytes of data to be digested */ CK_BYTE_PTR pDigest, /* receives the message digest */ CK_ULONG_PTR pulDigestLen) /* receives byte length of digest */ { CK_RV rv; struct sc_pkcs11_session *session; CK_ULONG ulBuflen = 0; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; sc_log(context, "C_Digest(hSession=0x%lx)", hSession); rv = get_session(hSession, &session); if (rv != CKR_OK) goto out; /* if pDigest == NULL, buffer size request */ if (pDigest) { /* As per PKCS#11 2.20 we need to check if buffer too small before update */ rv = sc_pkcs11_md_final(session, NULL, &ulBuflen); if (rv != CKR_OK) goto out; if (ulBuflen > *pulDigestLen) { *pulDigestLen = ulBuflen; rv = CKR_BUFFER_TOO_SMALL; goto out; } rv = sc_pkcs11_md_update(session, pData, ulDataLen); } if (rv == CKR_OK) rv = sc_pkcs11_md_final(session, pDigest, pulDigestLen); out: SC_LOG_RV("C_Digest = %s", rv); sc_pkcs11_unlock(); return rv; } CK_RV C_DigestUpdate(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_BYTE_PTR pPart, /* data to be digested */ CK_ULONG ulPartLen) /* bytes of data to be digested */ { 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_md_update(session, pPart, ulPartLen); SC_LOG_RV("C_DigestUpdate() = %s", rv); sc_pkcs11_unlock(); return rv; } CK_RV C_DigestKey(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_OBJECT_HANDLE hKey) /* handle of secret key to digest */ { return CKR_FUNCTION_NOT_SUPPORTED; } CK_RV C_DigestFinal(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_BYTE_PTR pDigest, /* receives the message digest */ CK_ULONG_PTR pulDigestLen) /* receives byte count of digest */ { 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_md_final(session, pDigest, pulDigestLen); SC_LOG_RV("C_DigestFinal() = %s", rv); sc_pkcs11_unlock(); return rv; } CK_RV C_SignInit(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_MECHANISM_PTR pMechanism, /* the signature mechanism */ CK_OBJECT_HANDLE hKey) /* handle of the signature key */ { CK_BBOOL can_sign; CK_KEY_TYPE key_type; CK_ATTRIBUTE sign_attribute = { CKA_SIGN, &can_sign, sizeof(can_sign) }; 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->sign == NULL_PTR) { rv = CKR_KEY_TYPE_INCONSISTENT; goto out; } rv = object->ops->get_attribute(session, object, &sign_attribute); if (rv != CKR_OK || !can_sign) { 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_sign_init(session, pMechanism, object, key_type); out: SC_LOG_RV("C_SignInit() = %s", rv); sc_pkcs11_unlock(); return rv; } CK_RV C_Sign(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_BYTE_PTR pData, /* the data (digest) to be signed */ CK_ULONG ulDataLen, /* count of bytes to be signed */ CK_BYTE_PTR pSignature, /* receives the signature */ CK_ULONG_PTR pulSignatureLen) /* receives byte count of signature */ { CK_RV rv; struct sc_pkcs11_session *session; CK_ULONG length; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; rv = get_session(hSession, &session); if (rv != CKR_OK) goto out; /* According to the pkcs11 specs, we must not do any calls that * change our crypto state if the caller is just asking for the * signature buffer size, or if the result would be * CKR_BUFFER_TOO_SMALL. Thus we cannot do the sign_update call * below. */ if ((rv = sc_pkcs11_sign_size(session, &length)) != CKR_OK) goto out; if (pSignature == NULL || length > *pulSignatureLen) { *pulSignatureLen = length; rv = pSignature ? CKR_BUFFER_TOO_SMALL : CKR_OK; goto out; } rv = sc_pkcs11_sign_update(session, pData, ulDataLen); if (rv == CKR_OK) { rv = restore_login_state(session->slot); if (rv == CKR_OK) rv = sc_pkcs11_sign_final(session, pSignature, pulSignatureLen); rv = reset_login_state(session->slot, rv); } out: SC_LOG_RV("C_Sign() = %s", rv); sc_pkcs11_unlock(); return rv; } CK_RV C_SignUpdate(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_BYTE_PTR pPart, /* the data (digest) to be signed */ CK_ULONG ulPartLen) /* count of bytes to be signed */ { 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_sign_update(session, pPart, ulPartLen); SC_LOG_RV("C_SignUpdate() = %s", rv); sc_pkcs11_unlock(); return rv; } CK_RV C_SignFinal(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_BYTE_PTR pSignature, /* receives the signature */ CK_ULONG_PTR pulSignatureLen) /* receives byte count of signature */ { struct sc_pkcs11_session *session; CK_ULONG length; CK_RV rv; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; rv = get_session(hSession, &session); if (rv != CKR_OK) goto out; /* According to the pkcs11 specs, we must not do any calls that * change our crypto state if the caller is just asking for the * signature buffer size, or if the result would be * CKR_BUFFER_TOO_SMALL. */ if ((rv = sc_pkcs11_sign_size(session, &length)) != CKR_OK) goto out; if (pSignature == NULL || length > *pulSignatureLen) { *pulSignatureLen = length; rv = pSignature ? CKR_BUFFER_TOO_SMALL : CKR_OK; } else { rv = restore_login_state(session->slot); if (rv == CKR_OK) rv = sc_pkcs11_sign_final(session, pSignature, pulSignatureLen); rv = reset_login_state(session->slot, rv); } out: SC_LOG_RV("C_SignFinal() = %s", rv); sc_pkcs11_unlock(); return rv; } CK_RV C_SignRecoverInit(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_MECHANISM_PTR pMechanism, /* the signature mechanism */ CK_OBJECT_HANDLE hKey) /* handle of the signature key */ { return CKR_FUNCTION_NOT_SUPPORTED; } CK_RV C_SignRecover(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_BYTE_PTR pData, /* the data (digest) to be signed */ CK_ULONG ulDataLen, /* count of bytes to be signed */ CK_BYTE_PTR pSignature, /* receives the signature */ CK_ULONG_PTR pulSignatureLen) /* receives byte count of signature */ { return CKR_FUNCTION_NOT_SUPPORTED; } CK_RV 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 */ { 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; } CK_RV C_Encrypt(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_BYTE_PTR pData, /* the plaintext data */ CK_ULONG ulDataLen, /* bytes of plaintext data */ CK_BYTE_PTR pEncryptedData, /* receives encrypted data */ CK_ULONG_PTR pulEncryptedDataLen) { /* receives encrypted 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_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 */ CK_BYTE_PTR pPart, /* the plaintext data */ CK_ULONG ulPartLen, /* bytes of plaintext data */ CK_BYTE_PTR pEncryptedPart, /* receives encrypted data */ CK_ULONG_PTR pulEncryptedPartLen) { /* receives encrypted 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_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 */ 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 */ CK_MECHANISM_PTR pMechanism, /* the decryption mechanism */ CK_OBJECT_HANDLE hKey) { /* handle of the decryption key */ CK_BBOOL can_decrypt, can_unwrap; CK_KEY_TYPE key_type; CK_ATTRIBUTE decrypt_attribute = { CKA_DECRYPT, &can_decrypt, sizeof(can_decrypt) }; CK_ATTRIBUTE key_type_attr = { CKA_KEY_TYPE, &key_type, sizeof(key_type) }; CK_ATTRIBUTE unwrap_attribute = { CKA_UNWRAP, &can_unwrap, sizeof(can_unwrap) }; 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->decrypt == NULL_PTR) { rv = CKR_KEY_TYPE_INCONSISTENT; goto out; } rv = object->ops->get_attribute(session, object, &decrypt_attribute); if (rv != CKR_OK || !can_decrypt) { /* Also accept UNWRAP - apps call Decrypt when they mean Unwrap */ 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; } rv = sc_pkcs11_decr_init(session, pMechanism, object, key_type); out: 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 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(session, pEncryptedData, ulEncryptedDataLen, pData, pulDataLen); } rv = reset_login_state(session->slot, 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 */ { 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 */ { 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 */ CK_BYTE_PTR pPart, /* the plaintext data */ CK_ULONG ulPartLen, /* bytes of plaintext data */ CK_BYTE_PTR pEncryptedPart, /* receives encrypted data */ CK_ULONG_PTR pulEncryptedPartLen) { /* receives encrypted byte count */ return CKR_FUNCTION_NOT_SUPPORTED; } CK_RV C_DecryptDigestUpdate(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_SignEncryptUpdate(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_BYTE_PTR pPart, /* the plaintext data */ CK_ULONG ulPartLen, /* bytes of plaintext data */ CK_BYTE_PTR pEncryptedPart, /* receives encrypted data */ CK_ULONG_PTR pulEncryptedPartLen) { /* receives encrypted byte count */ return CKR_FUNCTION_NOT_SUPPORTED; } CK_RV C_DecryptVerifyUpdate(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_BYTE_PTR pEncryptedPart, /* input encrypted data */ CK_ULONG ulEncryptedPartLen, /* count of byes 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_GenerateKey(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_MECHANISM_PTR pMechanism, /* the key generation mechanism */ CK_ATTRIBUTE_PTR pTemplate, /* template for the new key */ CK_ULONG ulCount, /* number of attributes in template */ CK_OBJECT_HANDLE_PTR phKey) { /* receives handle of new key */ return CKR_FUNCTION_NOT_SUPPORTED; } CK_RV C_GenerateKeyPair(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_MECHANISM_PTR pMechanism, /* the key gen. mech. */ CK_ATTRIBUTE_PTR pPublicKeyTemplate, /* pub. attr. template */ CK_ULONG ulPublicKeyAttributeCount, /* # of pub. attrs. */ CK_ATTRIBUTE_PTR pPrivateKeyTemplate, /* priv. attr. template */ CK_ULONG ulPrivateKeyAttributeCount, /* # of priv. attrs. */ CK_OBJECT_HANDLE_PTR phPublicKey, /* gets pub. key handle */ CK_OBJECT_HANDLE_PTR phPrivateKey) { /* gets priv. key handle */ CK_RV rv; struct sc_pkcs11_session *session; struct sc_pkcs11_slot *slot; if (pMechanism == NULL_PTR || (pPublicKeyTemplate == NULL_PTR && ulPublicKeyAttributeCount > 0) || (pPrivateKeyTemplate == NULL_PTR && ulPrivateKeyAttributeCount > 0)) return CKR_ARGUMENTS_BAD; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; dump_template(SC_LOG_DEBUG_NORMAL, "C_GenerateKeyPair(), PrivKey attrs", pPrivateKeyTemplate, ulPrivateKeyAttributeCount); dump_template(SC_LOG_DEBUG_NORMAL, "C_GenerateKeyPair(), PubKey attrs", pPublicKeyTemplate, ulPublicKeyAttributeCount); rv = get_session(hSession, &session); if (rv != CKR_OK) goto out; if (!(session->flags & CKF_RW_SESSION)) { rv = CKR_SESSION_READ_ONLY; goto out; } slot = session->slot; 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); if (rv == CKR_OK) rv = slot->p11card->framework->gen_keypair(slot, pMechanism, pPublicKeyTemplate, ulPublicKeyAttributeCount, pPrivateKeyTemplate, ulPrivateKeyAttributeCount, phPublicKey, phPrivateKey); rv = reset_login_state(session->slot, rv); } out: sc_pkcs11_unlock(); return rv; } CK_RV C_WrapKey(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_MECHANISM_PTR pMechanism, /* the wrapping mechanism */ CK_OBJECT_HANDLE hWrappingKey, /* handle of the wrapping key */ CK_OBJECT_HANDLE hKey, /* handle of the key to be wrapped */ CK_BYTE_PTR pWrappedKey, /* receives the wrapped key */ CK_ULONG_PTR pulWrappedKeyLen) { /* receives byte size of wrapped key */ 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 */ CK_MECHANISM_PTR pMechanism, /* the unwrapping mechanism */ CK_OBJECT_HANDLE hUnwrappingKey, /* handle of the unwrapping key */ CK_BYTE_PTR pWrappedKey, /* the wrapped key */ CK_ULONG ulWrappedKeyLen, /* bytes length of wrapped key */ CK_ATTRIBUTE_PTR pTemplate, /* template for the new key */ CK_ULONG ulAttributeCount, /* # of attributes in template */ CK_OBJECT_HANDLE_PTR phKey) { /* gets handle of recovered key */ 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 */ CK_MECHANISM_PTR pMechanism, /* the key derivation mechanism */ CK_OBJECT_HANDLE hBaseKey, /* handle of the base key */ CK_ATTRIBUTE_PTR pTemplate, /* template for the new key */ CK_ULONG ulAttributeCount, /* # of attributes in template */ CK_OBJECT_HANDLE_PTR phKey) /* gets handle of derived key */ { /* TODO: -DEE ECDH with Cofactor on PIV is an example */ /* TODO: need to do a lot of checking, will only support ECDH for now.*/ CK_RV rv; CK_BBOOL can_derive; CK_KEY_TYPE key_type; CK_ATTRIBUTE derive_attribute = { CKA_DERIVE, &can_derive, sizeof(can_derive) }; 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, hBaseKey, &session, &object); if (rv != CKR_OK) { if (rv == CKR_OBJECT_HANDLE_INVALID) rv = CKR_KEY_HANDLE_INVALID; goto out; } if (object->ops->derive == NULL_PTR) { rv = CKR_KEY_TYPE_INCONSISTENT; goto out; } rv = object->ops->get_attribute(session, object, &derive_attribute); if (rv != CKR_OK || !can_derive) { 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; } /* TODO DEE Should also check SENSITIVE, ALWAYS_SENSITIVE, EXTRACTABLE, NEVER_EXTRACTABLE of the BaseKey against the template for the newkey. */ switch(key_type) { case CKK_EC: case CKK_EC_MONTGOMERY: 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_deri(session, pMechanism, object, key_type, hSession, *phKey, key_object); /* TODO if (rv != CK_OK) need to destroy the object */ rv = reset_login_state(session->slot, rv); break; default: rv = CKR_KEY_TYPE_INCONSISTENT; } out: sc_pkcs11_unlock(); return rv; } CK_RV C_SeedRandom(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_BYTE_PTR pSeed, /* the seed material */ CK_ULONG ulSeedLen) { /* count of bytes of seed material */ return CKR_FUNCTION_NOT_SUPPORTED; } CK_RV C_GenerateRandom(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_BYTE_PTR RandomData, /* receives the random data */ CK_ULONG ulRandomLen) { /* number of bytes to be generated */ CK_RV rv; struct sc_pkcs11_session *session; struct sc_pkcs11_slot *slot; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; rv = get_session(hSession, &session); if (rv == CKR_OK) { slot = session->slot; 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; } CK_RV C_GetFunctionStatus(CK_SESSION_HANDLE hSession) { /* the session's handle */ return CKR_FUNCTION_NOT_PARALLEL; } CK_RV C_CancelFunction(CK_SESSION_HANDLE hSession) { /* the session's handle */ return CKR_FUNCTION_NOT_PARALLEL; } CK_RV C_VerifyInit(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_MECHANISM_PTR pMechanism, /* the verification mechanism */ CK_OBJECT_HANDLE hKey) { /* handle of the verification key */ #ifndef ENABLE_OPENSSL return CKR_FUNCTION_NOT_SUPPORTED; #else CK_KEY_TYPE key_type; CK_ATTRIBUTE key_type_attr = { CKA_KEY_TYPE, &key_type, sizeof(key_type) }; CK_RV rv; struct sc_pkcs11_session *session; struct sc_pkcs11_object *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, hKey, &session, &object); if (rv != CKR_OK) { if (rv == CKR_OBJECT_HANDLE_INVALID) rv = CKR_KEY_HANDLE_INVALID; 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_verif_init(session, pMechanism, object, key_type); out: SC_LOG_RV("C_VerifyInit() = %s", rv); sc_pkcs11_unlock(); return rv; #endif } CK_RV C_Verify(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_BYTE_PTR pData, /* plaintext data (digest) to compare */ CK_ULONG ulDataLen, /* length of data (digest) in bytes */ CK_BYTE_PTR pSignature, /* the signature to be verified */ CK_ULONG ulSignatureLen) { /* count of bytes of signature */ #ifndef ENABLE_OPENSSL return CKR_FUNCTION_NOT_SUPPORTED; #else 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) goto out; rv = sc_pkcs11_verif_update(session, pData, ulDataLen); if (rv == CKR_OK) { rv = restore_login_state(session->slot); if (rv == CKR_OK) rv = sc_pkcs11_verif_final(session, pSignature, ulSignatureLen); rv = reset_login_state(session->slot, rv); } out: SC_LOG_RV("C_Verify() = %s", rv); sc_pkcs11_unlock(); return rv; #endif } CK_RV C_VerifyUpdate(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_BYTE_PTR pPart, /* plaintext data (digest) to compare */ CK_ULONG ulPartLen) { /* length of data (digest) in bytes */ #ifndef ENABLE_OPENSSL return CKR_FUNCTION_NOT_SUPPORTED; #else 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_verif_update(session, pPart, ulPartLen); SC_LOG_RV("C_VerifyUpdate() = %s", rv); sc_pkcs11_unlock(); return rv; #endif } CK_RV C_VerifyFinal(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_BYTE_PTR pSignature, /* the signature to be verified */ CK_ULONG ulSignatureLen) { /* count of bytes of signature */ #ifndef ENABLE_OPENSSL return CKR_FUNCTION_NOT_SUPPORTED; #else 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_verif_final(session, pSignature, ulSignatureLen); rv = reset_login_state(session->slot, rv); } SC_LOG_RV("C_VerifyFinal() = %s", rv); sc_pkcs11_unlock(); return rv; #endif } CK_RV C_VerifyRecoverInit(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_MECHANISM_PTR pMechanism, /* the verification mechanism */ CK_OBJECT_HANDLE hKey) { /* handle of the verification key */ return CKR_FUNCTION_NOT_SUPPORTED; } CK_RV C_VerifyRecover(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_BYTE_PTR pSignature, /* the signature to be verified */ CK_ULONG ulSignatureLen, /* count of bytes of signature */ CK_BYTE_PTR pData, /* receives decrypted data (digest) */ CK_ULONG_PTR pulDataLen) { /* receives byte count of data */ 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; } /* * Helper function to compare attributes on any sort of object */ CK_RV sc_pkcs11_any_cmp_attribute(struct sc_pkcs11_session *session, void *ptr, CK_ATTRIBUTE_PTR attr) { CK_RV rv; struct sc_pkcs11_object *object; u8 temp1[1024]; u8 *temp2 = NULL; /* dynamic allocation for large attributes */ CK_ATTRIBUTE temp_attr; object = (struct sc_pkcs11_object *)ptr; temp_attr.type = attr->type; temp_attr.pValue = NULL; temp_attr.ulValueLen = 0; /* Get the length of the attribute */ rv = object->ops->get_attribute(session, object, &temp_attr); if (rv != CKR_OK || temp_attr.ulValueLen != attr->ulValueLen) return 0; if (temp_attr.ulValueLen <= sizeof(temp1)) temp_attr.pValue = temp1; else { temp2 = calloc(1, temp_attr.ulValueLen); if (temp2 == NULL) return 0; temp_attr.pValue = temp2; } /* Get the attribute */ rv = object->ops->get_attribute(session, object, &temp_attr); if (rv != CKR_OK) { rv = 0; goto done; } #ifdef DEBUG { char foo[64]; snprintf(foo, sizeof(foo), "Object %p (slot 0x%lx)", object, session->slot->id); dump_template(SC_LOG_DEBUG_NORMAL, foo, &temp_attr, 1); } #endif rv = temp_attr.ulValueLen == attr->ulValueLen && !memcmp(temp_attr.pValue, attr->pValue, attr->ulValueLen); done: if (temp2 != NULL) free(temp2); return rv; } OpenSC-0.26.1/src/pkcs11/pkcs11-opensc.h000066400000000000000000000020231474147347300173730ustar00rootroot00000000000000#ifndef PKCS11_OPENSC_H #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 distinction between * 'signature' and 'qualified signature' key slots. */ #define CKA_OPENSC_NON_REPUDIATION (CKA_VENDOR_DEFINED | SC_VENDOR_DEFINED | 1UL) #define CKA_SPKI (CKA_VENDOR_DEFINED | SC_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) #endif OpenSC-0.26.1/src/pkcs11/pkcs11-session.c000066400000000000000000000337631474147347300176010ustar00rootroot00000000000000/* * pkcs11-session.c: PKCS#11 functions for session management * * Copyright (C) 2001 Timo Teräs * * 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 "sc-pkcs11.h" CK_RV get_session(CK_SESSION_HANDLE hSession, struct sc_pkcs11_session **session) { *session = list_seek(&sessions, &hSession); if (!*session) return CKR_SESSION_HANDLE_INVALID; return CKR_OK; } CK_RV C_OpenSession(CK_SLOT_ID slotID, /* the slot's ID */ CK_FLAGS flags, /* defined in CK_SESSION_INFO */ CK_VOID_PTR pApplication, /* pointer passed to callback */ CK_NOTIFY Notify, /* notification callback function */ CK_SESSION_HANDLE_PTR phSession) { /* receives new session handle */ CK_RV rv; struct sc_pkcs11_slot *slot; struct sc_pkcs11_session *session; if (!(flags & CKF_SERIAL_SESSION)) return CKR_SESSION_PARALLEL_NOT_SUPPORTED; if (flags & ~(CKF_SERIAL_SESSION | CKF_RW_SESSION)) return CKR_ARGUMENTS_BAD; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; sc_log(context, "C_OpenSession(0x%lx)", slotID); rv = slot_get_token(slotID, &slot); if (rv != CKR_OK) goto out; /* 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; } session = (struct sc_pkcs11_session *)calloc(1, sizeof(struct sc_pkcs11_session)); if (session == NULL) { rv = CKR_HOST_MEMORY; goto out; } /* make session handle from pointer and check its uniqueness */ session->handle = (CK_SESSION_HANDLE)(uintptr_t)session; if (list_seek(&sessions, &session->handle) != NULL) { sc_log(context, "C_OpenSession handle 0x%lx already exists", session->handle); free(session); rv = CKR_HOST_MEMORY; goto out; } session->slot = slot; session->notify_callback = Notify; session->notify_data = pApplication; session->flags = flags; slot->nsessions++; list_append(&sessions, session); *phSession = session->handle; sc_log(context, "C_OpenSession handle: 0x%lx", session->handle); out: SC_LOG_RV("C_OpenSession() = %s", rv); sc_pkcs11_unlock(); return rv; } /* Internal version of C_CloseSession that gets called with * the global lock held */ static CK_RV sc_pkcs11_close_session(CK_SESSION_HANDLE hSession) { struct sc_pkcs11_slot *slot; struct sc_pkcs11_session *session; sc_log(context, "real C_CloseSession(0x%lx)", hSession); session = list_seek(&sessions, &hSession); if (!session) return CKR_SESSION_HANDLE_INVALID; /* If we're the last session using this slot, make sure * we log out */ slot = session->slot; slot->nsessions--; if (slot->nsessions == 0 && slot->login_user >= 0) { slot->login_user = -1; if (sc_pkcs11_conf.atomic) pop_all_login_states(slot); 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!"); free(session); return CKR_OK; } /* Internal version of C_CloseAllSessions that gets called with * the global lock held */ CK_RV sc_pkcs11_close_all_sessions(CK_SLOT_ID slotID) { CK_RV rv = CKR_OK, error; struct sc_pkcs11_session *session; unsigned int i; sc_log(context, "real C_CloseAllSessions(0x%lx) %d", slotID, list_size(&sessions)); for (i = 0; i < list_size(&sessions); i++) { session = list_get_at(&sessions, i); if (session->slot->id == slotID) if ((error = sc_pkcs11_close_session(session->handle)) != CKR_OK) rv = error; } return rv; } CK_RV C_CloseSession(CK_SESSION_HANDLE hSession) { /* the session's handle */ CK_RV rv; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; sc_log(context, "C_CloseSession(0x%lx)", hSession); rv = sc_pkcs11_close_session(hSession); sc_pkcs11_unlock(); return rv; } CK_RV C_CloseAllSessions(CK_SLOT_ID slotID) { /* the token's slot */ CK_RV rv; struct sc_pkcs11_slot *slot; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; sc_log(context, "C_CloseAllSessions(0x%lx)", slotID); rv = slot_get_token(slotID, &slot); if (rv != CKR_OK) goto out; rv = sc_pkcs11_close_all_sessions(slotID); out: sc_pkcs11_unlock(); 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; const char *name; int card_status = 0, logged_out = 0; if (pInfo == NULL_PTR) return CKR_ARGUMENTS_BAD; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; sc_log(context, "C_GetSessionInfo(hSession:0x%lx)", hSession); session = list_seek(&sessions, &hSession); if (!session) { rv = CKR_SESSION_HANDLE_INVALID; goto out; } sc_log(context, "C_GetSessionInfo(slot:0x%lx)", session->slot->id); pInfo->slotID = session->slot->id; pInfo->flags = session->flags; pInfo->ulDeviceError = 0; slot = session->slot; 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)) { pInfo->state = (session->flags & CKF_RW_SESSION) ? CKS_RW_USER_FUNCTIONS : CKS_RO_USER_FUNCTIONS; } else { pInfo->state = (session->flags & CKF_RW_SESSION) ? CKS_RW_PUBLIC_SESSION : CKS_RO_PUBLIC_SESSION; } out: 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; } CK_RV C_GetOperationState(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_BYTE_PTR pOperationState, /* location receiving state */ CK_ULONG_PTR pulOperationStateLen) { /* location receiving state length */ return CKR_FUNCTION_NOT_SUPPORTED; } CK_RV C_SetOperationState(CK_SESSION_HANDLE hSession, /* the session's handle */ CK_BYTE_PTR pOperationState, /* the location holding the state */ CK_ULONG ulOperationStateLen, /* location holding state length */ CK_OBJECT_HANDLE hEncryptionKey, /* handle of en/decryption key */ CK_OBJECT_HANDLE hAuthenticationKey) { /* handle of sign/verify key */ return CKR_FUNCTION_NOT_SUPPORTED; } CK_RV C_Login(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_RV rv; struct sc_pkcs11_session *session; struct sc_pkcs11_slot *slot; if (pPin == NULL_PTR && ulPinLen > 0) return CKR_ARGUMENTS_BAD; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; if (userType != CKU_USER && userType != CKU_SO && userType != CKU_CONTEXT_SPECIFIC) { rv = CKR_USER_TYPE_INVALID; goto out; } session = list_seek(&sessions, &hSession); if (!session) { rv = CKR_SESSION_HANDLE_INVALID; goto out; } sc_log(context, "C_Login(0x%lx, %lu)", hSession, userType); slot = session->slot; if (!(slot->token_info.flags & CKF_USER_PIN_INITIALIZED) && userType == CKU_USER) { rv = CKR_USER_PIN_NOT_INITIALIZED; goto out; } /* TODO: check if context specific is valid */ if (userType == CKU_CONTEXT_SPECIFIC) { if (slot->login_user == -1) { rv = CKR_OPERATION_NOT_INITIALIZED; } else { rv = restore_login_state(slot); if (rv == CKR_OK && slot->p11card && slot->p11card->framework) rv = slot->p11card->framework->login(slot, userType, pPin, ulPinLen); rv = reset_login_state(slot, rv); } } else { sc_log(context, "C_Login() slot->login_user %i", slot->login_user); if (slot->login_user >= 0) { if ((CK_USER_TYPE) slot->login_user == userType) rv = CKR_USER_ALREADY_LOGGED_IN; else rv = CKR_USER_ANOTHER_ALREADY_LOGGED_IN; goto out; } 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 = (int) userType; } rv = reset_login_state(slot, rv); } out: sc_pkcs11_unlock(); 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; struct sc_pkcs11_session *session; struct sc_pkcs11_slot *slot; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; session = list_seek(&sessions, &hSession); if (!session) { rv = CKR_SESSION_HANDLE_INVALID; goto out; } sc_log(context, "C_Logout(hSession:0x%lx)", hSession); slot = session->slot; if (slot->login_user >= 0) { slot->login_user = -1; if (sc_pkcs11_conf.atomic) pop_all_login_states(slot); else { if (!slot->p11card) return CKR_TOKEN_NOT_RECOGNIZED; rv = slot->p11card->framework->logout(slot); } } else rv = CKR_USER_NOT_LOGGED_IN; out: sc_pkcs11_unlock(); return rv; } CK_RV C_InitPIN(CK_SESSION_HANDLE hSession, CK_CHAR_PTR pPin, CK_ULONG ulPinLen) { CK_RV rv; struct sc_pkcs11_session *session; struct sc_pkcs11_slot *slot; sc_log(context, "C_InitPIN() called, pin '%s'", pPin ? (char *) pPin : ""); if (pPin == NULL_PTR && ulPinLen > 0) return CKR_ARGUMENTS_BAD; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; session = list_seek(&sessions, &hSession); if (!session) { rv = CKR_SESSION_HANDLE_INVALID; goto out; } if (!(session->flags & CKF_RW_SESSION)) { rv = CKR_SESSION_READ_ONLY; goto out; } slot = session->slot; if (slot->login_user != CKU_SO) { rv = CKR_USER_NOT_LOGGED_IN; } else if (slot->p11card == NULL || slot->p11card->framework->init_pin == NULL) { rv = CKR_FUNCTION_NOT_SUPPORTED; } else { rv = restore_login_state(slot); if (rv == CKR_OK) { rv = slot->p11card->framework->init_pin(slot, pPin, ulPinLen); sc_log(context, "C_InitPIN() init-pin result %li", rv); } rv = reset_login_state(slot, rv); } out: sc_pkcs11_unlock(); return rv; } CK_RV C_SetPIN(CK_SESSION_HANDLE hSession, CK_CHAR_PTR pOldPin, CK_ULONG ulOldLen, CK_CHAR_PTR pNewPin, CK_ULONG ulNewLen) { CK_RV rv; struct sc_pkcs11_session *session; struct sc_pkcs11_slot *slot; if ((pOldPin == NULL_PTR && ulOldLen > 0) || (pNewPin == NULL_PTR && ulNewLen > 0)) return CKR_ARGUMENTS_BAD; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; session = list_seek(&sessions, &hSession); if (!session) { rv = CKR_SESSION_HANDLE_INVALID; goto out; } slot = session->slot; sc_log(context, "Changing PIN (session 0x%lx; login user %d)", hSession, slot->login_user); if (!(session->flags & CKF_RW_SESSION)) { rv = CKR_SESSION_READ_ONLY; goto out; } rv = restore_login_state(slot); 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: sc_pkcs11_unlock(); return rv; } OpenSC-0.26.1/src/pkcs11/pkcs11-spy.c000066400000000000000000001756001474147347300167260ustar00rootroot00000000000000/* * Copyright (C) 2015 Mathias Brossard * * 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 #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 /* 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; /* Real Module Function List */ static CK_FUNCTION_LIST_3_0_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; static void * allocate_function_list(int v3) { CK_FUNCTION_LIST_PTR list = NULL; CK_FUNCTION_LIST_3_0_PTR list_3_0 = NULL; if (v3) { list = malloc(sizeof(CK_FUNCTION_LIST_3_0)); } 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 (!v3) { 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; return list_3_0; } /* 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] = { {"PKCS 11", NULL, 0} }; CK_INTERFACE spy_interface = {"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; CK_RV rv = CKR_OK; #ifdef _WIN32 char temp_path[PATH_MAX], expanded_path[PATH_MAX]; DWORD temp_len, expanded_len; long rc; HKEY hKey; #endif /* Allocates and initializes the pkcs11_spy structure */ pkcs11_spy = allocate_function_list(0); if (pkcs11_spy == NULL) { return CKR_HOST_MEMORY; } pkcs11_spy_3_0 = allocate_function_list(1); if (pkcs11_spy_3_0 == NULL) { free(pkcs11_spy); return CKR_HOST_MEMORY; } 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 * environment variables */ output = getenv("PKCS11SPY_OUTPUT"); if (output) spy_output = fopen(output, "a"); #ifdef _WIN32 if (!spy_output) { /* 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 ); if (rc != ERROR_SUCCESS ) rc = RegOpenKeyEx( HKEY_CURRENT_USER, "Software\\OpenSC Project\\PKCS11-Spy", 0, KEY_QUERY_VALUE, &hKey ); if( rc == ERROR_SUCCESS ) { temp_len = PATH_MAX; rc = RegQueryValueEx( hKey, "Output", NULL, NULL, (LPBYTE) temp_path, &temp_len); if (rc == ERROR_SUCCESS) { expanded_len = PATH_MAX; expanded_len = ExpandEnvironmentStrings(temp_path, expanded_path, expanded_len); if (expanded_len > 0) { memcpy(temp_path, expanded_path, PATH_MAX); temp_len = expanded_len; } } if( (rc == ERROR_SUCCESS) && (temp_len < PATH_MAX) ) output = temp_path; RegCloseKey( hKey ); } spy_output = fopen(output, "a"); } #endif if (!spy_output) spy_output = stderr; fprintf(spy_output, "\n\n*************** OpenSC PKCS#11 spy *****************\n"); module = getenv("PKCS11SPY"); #ifdef _WIN32 if (!module) { /* 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 ); if (rc != ERROR_SUCCESS) rc = RegOpenKeyEx( HKEY_CURRENT_USER, "Software\\OpenSC Project\\PKCS11-Spy", 0, KEY_QUERY_VALUE, &hKey ); if (rc == ERROR_SUCCESS) { temp_len = PATH_MAX; rc = RegQueryValueEx( hKey, "Module", NULL, NULL, (LPBYTE) temp_path, &temp_len); if (rc == ERROR_SUCCESS) { expanded_len = PATH_MAX; expanded_len = ExpandEnvironmentStrings(temp_path, expanded_path, expanded_len); if (expanded_len > 0) { memcpy(temp_path, expanded_path, PATH_MAX); temp_len = expanded_len; } } if( (rc == ERROR_SUCCESS) && (temp_len < PATH_MAX) ) module = temp_path; RegCloseKey( hKey ); } } #endif if (module == NULL) { fprintf(spy_output, "Error: no module specified. Please set PKCS11SPY environment.\n"); free(pkcs11_spy); return CKR_DEVICE_ERROR; } modhandle = C_LoadModule(module, &po_v2); po = (CK_FUNCTION_LIST_3_0_PTR) po_v2; if (modhandle && po) { fprintf(spy_output, "Loaded: \"%s\"\n", module); } else { po = NULL; free(pkcs11_spy); rv = CKR_GENERAL_ERROR; } return rv; } static void enter(const char *function) { static int count = 0; #ifdef _WIN32 SYSTEMTIME st; #else struct tm *tm; struct timeval tv; char time_string[40]; #endif fprintf(spy_output, "\n%d: %s\n", count++, function); #ifdef _WIN32 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, "P:%lu; T:0x%lu %s.%03ld\n", (unsigned long)getpid(), (unsigned long)pthread_self(), time_string, (long)tv.tv_usec / 1000); #endif } static CK_RV retne(CK_RV rv) { fprintf(spy_output, "Returned: %ld %s\n", (unsigned long) rv, lookup_enum (RV_T, rv )); fflush(spy_output); return rv; } 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); } 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); } static void spy_dump_ulong_in(const char *name, CK_ULONG value) { fprintf(spy_output, "[in] %s = 0x%lx\n", name, value); } static void spy_dump_ulong_out(const char *name, CK_ULONG value) { fprintf(spy_output, "[out] %s = 0x%lx\n", name, value); } static void spy_dump_desc_out(const char *name) { fprintf(spy_output, "[out] %s: \n", name); } static void spy_dump_array_out(const char *name, CK_ULONG size) { fprintf(spy_output, "[out] %s[%ld]: \n", name, size); } static void spy_attribute_req_in(const char *name, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) { fprintf(spy_output, "[in] %s[%ld]: \n", name, ulCount); print_attribute_list_req(spy_output, pTemplate, ulCount); } static void spy_attribute_list_in(const char *name, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) { fprintf(spy_output, "[in] %s[%ld]: \n", name, ulCount); print_attribute_list(spy_output, pTemplate, ulCount); } static void spy_attribute_list_out(const char *name, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) { fprintf(spy_output, "[out] %s[%ld]: \n", name, ulCount); print_attribute_list(spy_output, pTemplate, ulCount); } static void spy_dump_mechanism_in(const char *name, CK_MECHANISM_PTR pMechanism) { 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); } } switch (pMechanism->mechanism) { case CKM_AES_GCM: if (pMechanism->pParameter != NULL) { 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 { fprintf(spy_output, "[in] %s->pParameter = NULL\n", name); break; } break; case CKM_AES_CCM: if (pMechanism->pParameter != NULL) { 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); } else { fprintf(spy_output, "[in] %s->pParameter = NULL\n", name); break; } break; case CKM_ECDH1_DERIVE: case CKM_ECDH1_COFACTOR_DERIVE: if (pMechanism->pParameter != NULL) { 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); fprintf(spy_output, "[in] %s->pParameter->pPublicData[ulPublicDataLen] = ", name); print_generic(spy_output, 0, param->pPublicData, param->ulPublicDataLen, NULL); } else { fprintf(spy_output, "[in] %s->pParameter = NULL\n", name); break; } break; case CKM_ECMQV_DERIVE: if (pMechanism->pParameter != NULL) { 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); fprintf(spy_output, "%s->pParameter->pPublicData[ulPublicDataLen] = ", name); print_generic(spy_output, 0, param->pPublicData, param->ulPublicDataLen, NULL); 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); fprintf(spy_output, "%s->pParameter->publicKey = %lu", name, param->publicKey); } else { fprintf(spy_output, "[in] %s->pParameter = NULL\n", name); break; } break; case CKM_RSA_PKCS_OAEP: if (pMechanism->pParameter != NULL) { 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); } else { fprintf(spy_output, "[in] %s->pParameter = NULL\n", name); break; } 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->pParameter != NULL) { 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); } else { fprintf(spy_output, "[in] %s->pParameter = NULL\n", name); break; } 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) { if (po == NULL) { CK_RV rv = init_spy(); if (rv != CKR_OK) return rv; } enter("C_GetFunctionList"); if (ppFunctionList == NULL) return retne(CKR_ARGUMENTS_BAD); *ppFunctionList = pkcs11_spy; return retne(CKR_OK); } CK_RV C_Initialize(CK_VOID_PTR pInitArgs) { CK_RV rv; if (po == NULL) { rv = init_spy(); if (rv != CKR_OK) return rv; } enter("C_Initialize"); print_ptr_in("pInitArgs", pInitArgs); if (pInitArgs) { CK_C_INITIALIZE_ARGS *ptr = pInitArgs; fprintf(spy_output, " flags: %ld\n", ptr->flags); if (ptr->flags & CKF_LIBRARY_CANT_CREATE_OS_THREADS) fprintf(spy_output, " CKF_LIBRARY_CANT_CREATE_OS_THREADS\n"); if (ptr->flags & CKF_OS_LOCKING_OK) fprintf(spy_output, " CKF_OS_LOCKING_OK\n"); } rv = po->C_Initialize(pInitArgs); return retne(rv); } CK_RV C_Finalize(CK_VOID_PTR pReserved) { CK_RV rv; enter("C_Finalize"); rv = po->C_Finalize(pReserved); return retne(rv); } CK_RV C_GetInfo(CK_INFO_PTR pInfo) { CK_RV rv; enter("C_GetInfo"); rv = po->C_GetInfo(pInfo); if(rv == CKR_OK) { spy_dump_desc_out("pInfo"); print_ck_info(spy_output, pInfo); } return retne(rv); } CK_RV C_GetSlotList(CK_BBOOL tokenPresent, CK_SLOT_ID_PTR pSlotList, CK_ULONG_PTR pulCount) { CK_RV rv; enter("C_GetSlotList"); spy_dump_ulong_in("tokenPresent", tokenPresent); rv = po->C_GetSlotList(tokenPresent, pSlotList, pulCount); if(rv == CKR_OK) { spy_dump_desc_out("pSlotList"); print_slot_list(spy_output, pSlotList, *pulCount); spy_dump_ulong_out("*pulCount", *pulCount); } return retne(rv); } CK_RV C_GetSlotInfo(CK_SLOT_ID slotID, CK_SLOT_INFO_PTR pInfo) { CK_RV rv; enter("C_GetSlotInfo"); spy_dump_ulong_in("slotID", slotID); rv = po->C_GetSlotInfo(slotID, pInfo); if(rv == CKR_OK) { spy_dump_desc_out("pInfo"); print_slot_info(spy_output, pInfo); } return retne(rv); } CK_RV C_GetTokenInfo(CK_SLOT_ID slotID, CK_TOKEN_INFO_PTR pInfo) { CK_RV rv; enter("C_GetTokenInfo"); spy_dump_ulong_in("slotID", slotID); rv = po->C_GetTokenInfo(slotID, pInfo); if(rv == CKR_OK) { spy_dump_desc_out("pInfo"); print_token_info(spy_output, pInfo); } return retne(rv); } CK_RV C_GetMechanismList(CK_SLOT_ID slotID, CK_MECHANISM_TYPE_PTR pMechanismList, CK_ULONG_PTR pulCount) { CK_RV rv; enter("C_GetMechanismList"); spy_dump_ulong_in("slotID", slotID); rv = po->C_GetMechanismList(slotID, pMechanismList, pulCount); if(rv == CKR_OK) { spy_dump_array_out("pMechanismList", *pulCount); print_mech_list(spy_output, pMechanismList, *pulCount); } return retne(rv); } CK_RV C_GetMechanismInfo(CK_SLOT_ID slotID, CK_MECHANISM_TYPE type, CK_MECHANISM_INFO_PTR pInfo) { CK_RV rv; enter("C_GetMechanismInfo"); spy_dump_ulong_in("slotID", slotID); FPRINTF_LOOKUP_ENUM("[in] type = %s\n", MEC_T, type); rv = po->C_GetMechanismInfo(slotID, type, pInfo); if(rv == CKR_OK) { spy_dump_desc_out("pInfo"); print_mech_info(spy_output, type, pInfo); } return retne(rv); } CK_RV C_InitToken (CK_SLOT_ID slotID, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen, CK_UTF8CHAR_PTR pLabel) { CK_RV rv; enter("C_InitToken"); spy_dump_ulong_in("slotID", slotID); spy_dump_string_in("pPin[ulPinLen]", pPin, ulPinLen); spy_dump_string_in("pLabel[32]", pLabel, 32); rv = po->C_InitToken (slotID, pPin, ulPinLen, pLabel); return retne(rv); } CK_RV C_InitPIN(CK_SESSION_HANDLE hSession, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen) { CK_RV rv; enter("C_InitPIN"); spy_dump_ulong_in("hSession", hSession); spy_dump_string_in("pPin[ulPinLen]", pPin, ulPinLen); rv = po->C_InitPIN(hSession, pPin, ulPinLen); return retne(rv); } CK_RV C_SetPIN(CK_SESSION_HANDLE hSession, CK_UTF8CHAR_PTR pOldPin, CK_ULONG ulOldLen, CK_UTF8CHAR_PTR pNewPin, CK_ULONG ulNewLen) { CK_RV rv; enter("C_SetPIN"); spy_dump_ulong_in("hSession", hSession); spy_dump_string_in("pOldPin[ulOldLen]", pOldPin, ulOldLen); spy_dump_string_in("pNewPin[ulNewLen]", pNewPin, ulNewLen); rv = po->C_SetPIN(hSession, pOldPin, ulOldLen, pNewPin, ulNewLen); return retne(rv); } CK_RV C_OpenSession(CK_SLOT_ID slotID, CK_FLAGS flags, CK_VOID_PTR pApplication, CK_NOTIFY Notify, CK_SESSION_HANDLE_PTR phSession) { CK_RV rv; enter("C_OpenSession"); spy_dump_ulong_in("slotID", slotID); spy_dump_ulong_in("flags", flags); 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); if (phSession) spy_dump_ulong_out("*phSession", *phSession); else fprintf(spy_output, "[out] phSession = %p\n", phSession); return retne(rv); } CK_RV C_CloseSession(CK_SESSION_HANDLE hSession) { CK_RV rv; enter("C_CloseSession"); spy_dump_ulong_in("hSession", hSession); rv = po->C_CloseSession(hSession); return retne(rv); } CK_RV C_CloseAllSessions(CK_SLOT_ID slotID) { CK_RV rv; enter("C_CloseAllSessions"); spy_dump_ulong_in("slotID", slotID); rv = po->C_CloseAllSessions(slotID); return retne(rv); } CK_RV C_GetSessionInfo(CK_SESSION_HANDLE hSession, CK_SESSION_INFO_PTR pInfo) { CK_RV rv; enter("C_GetSessionInfo"); spy_dump_ulong_in("hSession", hSession); rv = po->C_GetSessionInfo(hSession, pInfo); if(rv == CKR_OK) { spy_dump_desc_out("pInfo"); print_session_info(spy_output, pInfo); } return retne(rv); } CK_RV C_GetOperationState(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pOperationState, CK_ULONG_PTR pulOperationStateLen) { CK_RV rv; enter("C_GetOperationState"); spy_dump_ulong_in("hSession", hSession); rv = po->C_GetOperationState(hSession, pOperationState, pulOperationStateLen); if (rv == CKR_OK) spy_dump_string_out("pOperationState[*pulOperationStateLen]", pOperationState, *pulOperationStateLen); return retne(rv); } CK_RV C_SetOperationState(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pOperationState, CK_ULONG ulOperationStateLen, CK_OBJECT_HANDLE hEncryptionKey, CK_OBJECT_HANDLE hAuthenticationKey) { CK_RV rv; enter("SetOperationState"); spy_dump_ulong_in("hSession", hSession); spy_dump_string_in("pOperationState[ulOperationStateLen]", pOperationState, ulOperationStateLen); spy_dump_ulong_in("hEncryptionKey", hEncryptionKey); spy_dump_ulong_in("hAuthenticationKey", hAuthenticationKey); rv = po->C_SetOperationState(hSession, pOperationState, ulOperationStateLen, hEncryptionKey, hAuthenticationKey); return retne(rv); } CK_RV C_Login(CK_SESSION_HANDLE hSession, CK_USER_TYPE userType, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen) { CK_RV rv; enter("C_Login"); spy_dump_ulong_in("hSession", hSession); 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); } CK_RV C_Logout(CK_SESSION_HANDLE hSession) { CK_RV rv; enter("C_Logout"); spy_dump_ulong_in("hSession", hSession); rv = po->C_Logout(hSession); return retne(rv); } CK_RV C_CreateObject(CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phObject) { CK_RV rv; enter("C_CreateObject"); spy_dump_ulong_in("hSession", hSession); spy_attribute_list_in("pTemplate", pTemplate, ulCount); rv = po->C_CreateObject(hSession, pTemplate, ulCount, phObject); if (rv == CKR_OK) spy_dump_ulong_out("*phObject", *phObject); return retne(rv); } CK_RV C_CopyObject(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phNewObject) { CK_RV rv; enter("C_CopyObject"); spy_dump_ulong_in("hSession", hSession); spy_dump_ulong_in("hObject", hObject); spy_attribute_list_in("pTemplate", pTemplate, ulCount); rv = po->C_CopyObject(hSession, hObject, pTemplate, ulCount, phNewObject); if (rv == CKR_OK) spy_dump_ulong_out("*phNewObject", *phNewObject); return retne(rv); } CK_RV C_DestroyObject(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject) { CK_RV rv; enter("C_DestroyObject"); spy_dump_ulong_in("hSession", hSession); spy_dump_ulong_in("hObject", hObject); rv = po->C_DestroyObject(hSession, hObject); return retne(rv); } CK_RV C_GetObjectSize(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_ULONG_PTR pulSize) { CK_RV rv; enter("C_GetObjectSize"); spy_dump_ulong_in("hSession", hSession); spy_dump_ulong_in("hObject", hObject); rv = po->C_GetObjectSize(hSession, hObject, pulSize); if (rv == CKR_OK) spy_dump_ulong_out("*pulSize", *pulSize); return retne(rv); } CK_RV C_GetAttributeValue(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) { CK_RV rv; enter("C_GetAttributeValue"); spy_dump_ulong_in("hSession", hSession); spy_dump_ulong_in("hObject", hObject); spy_attribute_req_in("pTemplate", pTemplate, ulCount); /* PKCS#11 says: * ``Note that the error codes CKR_ATTRIBUTE_SENSITIVE, * CKR_ATTRIBUTE_TYPE_INVALID, and CKR_BUFFER_TOO_SMALL do not denote * true errors for C_GetAttributeValue.'' * That's why we ignore these error codes, because we want to display * all other attributes anyway (they may have been returned correctly) */ rv = po->C_GetAttributeValue(hSession, hObject, pTemplate, ulCount); if (rv == CKR_OK || rv == CKR_ATTRIBUTE_SENSITIVE || rv == CKR_ATTRIBUTE_TYPE_INVALID || rv == CKR_BUFFER_TOO_SMALL) spy_attribute_list_out("pTemplate", pTemplate, ulCount); return retne(rv); } CK_RV C_SetAttributeValue(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) { CK_RV rv; enter("C_SetAttributeValue"); spy_dump_ulong_in("hSession", hSession); spy_dump_ulong_in("hObject", hObject); spy_attribute_list_in("pTemplate", pTemplate, ulCount); rv = po->C_SetAttributeValue(hSession, hObject, pTemplate, ulCount); return retne(rv); } CK_RV C_FindObjectsInit(CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) { CK_RV rv; enter("C_FindObjectsInit"); spy_dump_ulong_in("hSession", hSession); spy_attribute_list_in("pTemplate", pTemplate, ulCount); rv = po->C_FindObjectsInit(hSession, pTemplate, ulCount); return retne(rv); } CK_RV C_FindObjects(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE_PTR phObject, CK_ULONG ulMaxObjectCount, CK_ULONG_PTR pulObjectCount) { CK_RV rv; enter("C_FindObjects"); spy_dump_ulong_in("hSession", hSession); spy_dump_ulong_in("ulMaxObjectCount", ulMaxObjectCount); rv = po->C_FindObjects(hSession, phObject, ulMaxObjectCount, pulObjectCount); if (rv == CKR_OK) { CK_ULONG i; spy_dump_ulong_out("ulObjectCount", *pulObjectCount); for (i = 0; i < *pulObjectCount; i++) fprintf(spy_output, "Object 0x%lx matches\n", phObject[i]); } return retne(rv); } CK_RV C_FindObjectsFinal(CK_SESSION_HANDLE hSession) { CK_RV rv; enter("C_FindObjectsFinal"); spy_dump_ulong_in("hSession", hSession); rv = po->C_FindObjectsFinal(hSession); return retne(rv); } CK_RV C_EncryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) { CK_RV rv; enter("C_EncryptInit"); spy_dump_ulong_in("hSession", hSession); spy_dump_mechanism_in("pMechanism", pMechanism); spy_dump_ulong_in("hKey", hKey); rv = po->C_EncryptInit(hSession, pMechanism, hKey); return retne(rv); } CK_RV C_Encrypt(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pEncryptedData, CK_ULONG_PTR pulEncryptedDataLen) { CK_RV rv; enter("C_Encrypt"); 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) { spy_dump_string_out("pEncryptedData[*pulEncryptedDataLen]", pEncryptedData, *pulEncryptedDataLen); } else if (rv == CKR_BUFFER_TOO_SMALL) { spy_dump_ulong_out("pulEncryptedDataLen", *pulEncryptedDataLen); } return retne(rv); } CK_RV C_EncryptUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart, CK_ULONG_PTR pulEncryptedPartLen) { CK_RV rv; enter("C_EncryptUpdate"); spy_dump_ulong_in("hSession", hSession); spy_dump_string_in("pPart[ulPartLen]", pPart, ulPartLen); rv = po->C_EncryptUpdate(hSession, pPart, ulPartLen, pEncryptedPart, pulEncryptedPartLen); if (rv == CKR_OK) spy_dump_string_out("pEncryptedPart[*pulEncryptedPartLen]", pEncryptedPart, *pulEncryptedPartLen); return retne(rv); } CK_RV C_EncryptFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pLastEncryptedPart, CK_ULONG_PTR pulLastEncryptedPartLen) { CK_RV rv; enter("C_EncryptFinal"); spy_dump_ulong_in("hSession", hSession); rv = po->C_EncryptFinal(hSession, pLastEncryptedPart, pulLastEncryptedPartLen); 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); } CK_RV C_DecryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) { CK_RV rv; enter("C_DecryptInit"); spy_dump_ulong_in("hSession", hSession); spy_dump_mechanism_in("pMechanism", pMechanism); spy_dump_ulong_in("hKey", hKey); rv = po->C_DecryptInit(hSession, pMechanism, hKey); return retne(rv); } CK_RV C_Decrypt(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedData, CK_ULONG ulEncryptedDataLen, CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen) { CK_RV rv; enter("C_Decrypt"); 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) { spy_dump_string_out("pData[*pulDataLen]", pData, *pulDataLen); } else if (rv == CKR_BUFFER_TOO_SMALL) { spy_dump_ulong_out("pulDataLen", *pulDataLen); } return retne(rv); } CK_RV C_DecryptUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedPart, CK_ULONG ulEncryptedPartLen, CK_BYTE_PTR pPart, CK_ULONG_PTR pulPartLen) { CK_RV rv; enter("C_DecryptUpdate"); spy_dump_ulong_in("hSession", hSession); spy_dump_string_in("pEncryptedPart[ulEncryptedPartLen]", pEncryptedPart, ulEncryptedPartLen); rv = po->C_DecryptUpdate(hSession, pEncryptedPart, ulEncryptedPartLen, pPart, pulPartLen); if (rv == CKR_OK) spy_dump_string_out("pPart[*pulPartLen]", pPart, *pulPartLen); return retne(rv); } CK_RV C_DecryptFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pLastPart, CK_ULONG_PTR pulLastPartLen) { CK_RV rv; enter("C_DecryptFinal"); spy_dump_ulong_in("hSession", hSession); rv = po->C_DecryptFinal(hSession, pLastPart, pulLastPartLen); 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); } CK_RV C_DigestInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism) { CK_RV rv; enter("C_DigestInit"); spy_dump_ulong_in("hSession", hSession); spy_dump_mechanism_in("pMechanism", pMechanism); rv = po->C_DigestInit(hSession, pMechanism); return retne(rv); } CK_RV C_Digest(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pDigest, CK_ULONG_PTR pulDigestLen) { CK_RV rv; enter("C_Digest"); spy_dump_ulong_in("hSession", hSession); spy_dump_string_in("pData[ulDataLen]", pData, ulDataLen); rv = po->C_Digest(hSession, pData, ulDataLen, pDigest, pulDigestLen); if (rv == CKR_OK) spy_dump_string_out("pDigest[*pulDigestLen]", pDigest, *pulDigestLen); return retne(rv); } CK_RV C_DigestUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen) { CK_RV rv; enter("C_DigestUpdate"); spy_dump_ulong_in("hSession", hSession); spy_dump_string_in("pPart[ulPartLen]", pPart, ulPartLen); rv = po->C_DigestUpdate(hSession, pPart, ulPartLen); return retne(rv); } CK_RV C_DigestKey(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hKey) { CK_RV rv; enter("C_DigestKey"); spy_dump_ulong_in("hSession", hSession); spy_dump_ulong_in("hKey", hKey); rv = po->C_DigestKey(hSession, hKey); return retne(rv); } CK_RV C_DigestFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pDigest, CK_ULONG_PTR pulDigestLen) { CK_RV rv; enter("C_DigestFinal"); spy_dump_ulong_in("hSession", hSession); rv = po->C_DigestFinal(hSession, pDigest, pulDigestLen); if (rv == CKR_OK) spy_dump_string_out("pDigest[*pulDigestLen]", pDigest, *pulDigestLen); return retne(rv); } CK_RV C_SignInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) { CK_RV rv; enter("C_SignInit"); spy_dump_ulong_in("hSession", hSession); spy_dump_mechanism_in("pMechanism", pMechanism); spy_dump_ulong_in("hKey", hKey); rv = po->C_SignInit(hSession, pMechanism, hKey); return retne(rv); } CK_RV C_Sign(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) { CK_RV rv; enter("C_Sign"); 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) { spy_dump_string_out("pSignature[*pulSignatureLen]", pSignature, *pulSignatureLen); } else if (rv == CKR_BUFFER_TOO_SMALL) { spy_dump_ulong_out("pulSignatureLen", *pulSignatureLen); } return retne(rv); } CK_RV C_SignUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen) { CK_RV rv; enter("C_SignUpdate"); spy_dump_ulong_in("hSession", hSession); spy_dump_string_in("pPart[ulPartLen]", pPart, ulPartLen); rv = po->C_SignUpdate(hSession, pPart, ulPartLen); return retne(rv); } CK_RV C_SignFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) { CK_RV rv; enter("C_SignFinal"); spy_dump_ulong_in("hSession", hSession); rv = po->C_SignFinal(hSession, pSignature, pulSignatureLen); 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); } CK_RV C_SignRecoverInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) { CK_RV rv; enter("C_SignRecoverInit"); spy_dump_ulong_in("hSession", hSession); spy_dump_mechanism_in("pMechanism", pMechanism); spy_dump_ulong_in("hKey", hKey); rv = po->C_SignRecoverInit(hSession, pMechanism, hKey); return retne(rv); } CK_RV C_SignRecover(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) { CK_RV rv; enter("C_SignRecover"); 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) { spy_dump_string_out("pSignature[*pulSignatureLen]", pSignature, *pulSignatureLen); } else if (rv == CKR_BUFFER_TOO_SMALL) { spy_dump_ulong_out("pulSignatureLen", *pulSignatureLen); } return retne(rv); } CK_RV C_VerifyInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) { CK_RV rv; enter("C_VerifyInit"); spy_dump_ulong_in("hSession", hSession); spy_dump_mechanism_in("pMechanism", pMechanism); spy_dump_ulong_in("hKey", hKey); rv = po->C_VerifyInit(hSession, pMechanism, hKey); return retne(rv); } CK_RV C_Verify(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen) { CK_RV rv; enter("C_Verify"); spy_dump_ulong_in("hSession", hSession); spy_dump_string_in("pData[ulDataLen]", pData, ulDataLen); spy_dump_string_in("pSignature[ulSignatureLen]", pSignature, ulSignatureLen); rv = po->C_Verify(hSession, pData, ulDataLen, pSignature, ulSignatureLen); return retne(rv); } CK_RV C_VerifyUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen) { CK_RV rv; enter("C_VerifyUpdate"); spy_dump_ulong_in("hSession", hSession); spy_dump_string_in("pPart[ulPartLen]", pPart, ulPartLen); rv = po->C_VerifyUpdate(hSession, pPart, ulPartLen); return retne(rv); } CK_RV C_VerifyFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen) { CK_RV rv; enter("C_VerifyFinal"); spy_dump_ulong_in("hSession", hSession); spy_dump_string_in("pSignature[ulSignatureLen]", pSignature, ulSignatureLen); rv = po->C_VerifyFinal(hSession, pSignature, ulSignatureLen); return retne(rv); } CK_RV C_VerifyRecoverInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) { CK_RV rv; enter("C_VerifyRecoverInit"); spy_dump_ulong_in("hSession", hSession); spy_dump_mechanism_in("pMechanism", pMechanism); spy_dump_ulong_in("hKey", hKey); rv = po->C_VerifyRecoverInit(hSession, pMechanism, hKey); return retne(rv); } CK_RV C_VerifyRecover(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen, CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen) { CK_RV rv; enter("C_VerifyRecover"); spy_dump_ulong_in("hSession", hSession); spy_dump_string_in("pSignature[ulSignatureLen]", pSignature, ulSignatureLen); rv = po->C_VerifyRecover(hSession, pSignature, ulSignatureLen, pData, pulDataLen); if (rv == CKR_OK) spy_dump_string_out("pData[*pulDataLen]", pData, *pulDataLen); return retne(rv); } CK_RV C_DigestEncryptUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart, CK_ULONG_PTR pulEncryptedPartLen) { CK_RV rv; enter("C_DigestEncryptUpdate"); spy_dump_ulong_in("hSession", hSession); spy_dump_string_in("pPart[ulPartLen]", pPart, ulPartLen); rv = po->C_DigestEncryptUpdate(hSession, pPart, ulPartLen, pEncryptedPart, pulEncryptedPartLen); if (rv == CKR_OK) spy_dump_string_out("pEncryptedPart[*pulEncryptedPartLen]", pEncryptedPart, *pulEncryptedPartLen); return retne(rv); } CK_RV C_DecryptDigestUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedPart,CK_ULONG ulEncryptedPartLen, CK_BYTE_PTR pPart, CK_ULONG_PTR pulPartLen) { CK_RV rv; enter("C_DecryptDigestUpdate"); spy_dump_ulong_in("hSession", hSession); spy_dump_string_in("pEncryptedPart[ulEncryptedPartLen]", pEncryptedPart, ulEncryptedPartLen); rv = po->C_DecryptDigestUpdate(hSession, pEncryptedPart, ulEncryptedPartLen, pPart, pulPartLen); if (rv == CKR_OK) spy_dump_string_out("pPart[*pulPartLen]", pPart, *pulPartLen); return retne(rv); } CK_RV C_SignEncryptUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart, CK_ULONG_PTR pulEncryptedPartLen) { CK_RV rv; enter("C_SignEncryptUpdate"); spy_dump_ulong_in("hSession", hSession); spy_dump_string_in("pPart[ulPartLen]", pPart, ulPartLen); rv = po->C_SignEncryptUpdate(hSession, pPart, ulPartLen, pEncryptedPart, pulEncryptedPartLen); if (rv == CKR_OK) spy_dump_string_out("pEncryptedPart[*pulEncryptedPartLen]", pEncryptedPart, *pulEncryptedPartLen); return retne(rv); } CK_RV C_DecryptVerifyUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedPart, CK_ULONG ulEncryptedPartLen, CK_BYTE_PTR pPart, CK_ULONG_PTR pulPartLen) { CK_RV rv; enter("C_DecryptVerifyUpdate"); spy_dump_ulong_in("hSession", hSession); spy_dump_string_in("pEncryptedPart[ulEncryptedPartLen]", pEncryptedPart, ulEncryptedPartLen); rv = po->C_DecryptVerifyUpdate(hSession, pEncryptedPart, ulEncryptedPartLen, pPart, pulPartLen); if (rv == CKR_OK) spy_dump_string_out("pPart[*pulPartLen]", pPart, *pulPartLen); return retne(rv); } CK_RV C_GenerateKey(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phKey) { CK_RV rv; enter("C_GenerateKey"); spy_dump_ulong_in("hSession", hSession); spy_dump_mechanism_in("pMechanism", pMechanism); spy_attribute_list_in("pTemplate", pTemplate, ulCount); rv = po->C_GenerateKey(hSession, pMechanism, pTemplate, ulCount, phKey); if (rv == CKR_OK) spy_dump_ulong_out("hKey", *phKey); return retne(rv); } CK_RV C_GenerateKeyPair(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_ATTRIBUTE_PTR pPublicKeyTemplate, CK_ULONG ulPublicKeyAttributeCount, CK_ATTRIBUTE_PTR pPrivateKeyTemplate, CK_ULONG ulPrivateKeyAttributeCount, CK_OBJECT_HANDLE_PTR phPublicKey, CK_OBJECT_HANDLE_PTR phPrivateKey) { CK_RV rv; enter("C_GenerateKeyPair"); spy_dump_ulong_in("hSession", hSession); spy_dump_mechanism_in("pMechanism", pMechanism); spy_attribute_list_in("pPublicKeyTemplate", pPublicKeyTemplate, ulPublicKeyAttributeCount); spy_attribute_list_in("pPrivateKeyTemplate", pPrivateKeyTemplate, ulPrivateKeyAttributeCount); rv = po->C_GenerateKeyPair(hSession, pMechanism, pPublicKeyTemplate, ulPublicKeyAttributeCount, pPrivateKeyTemplate, ulPrivateKeyAttributeCount, phPublicKey, phPrivateKey); if (rv == CKR_OK) { spy_dump_ulong_out("hPublicKey", *phPublicKey); spy_dump_ulong_out("hPrivateKey", *phPrivateKey); } return retne(rv); } CK_RV C_WrapKey(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hWrappingKey, CK_OBJECT_HANDLE hKey, CK_BYTE_PTR pWrappedKey, CK_ULONG_PTR pulWrappedKeyLen) { CK_RV rv; enter("C_WrapKey"); spy_dump_ulong_in("hSession", hSession); spy_dump_mechanism_in("pMechanism", pMechanism); 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) { 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_UnwrapKey(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_OBJECT_HANDLE_PTR phKey) { CK_RV rv; enter("C_UnwrapKey"); spy_dump_ulong_in("hSession", hSession); spy_dump_mechanism_in("pMechanism", pMechanism); spy_dump_ulong_in("hUnwrappingKey", hUnwrappingKey); spy_dump_string_in("pWrappedKey[ulWrappedKeyLen]", pWrappedKey, ulWrappedKeyLen); spy_attribute_list_in("pTemplate", pTemplate, ulAttributeCount); rv = po->C_UnwrapKey(hSession, pMechanism, hUnwrappingKey, pWrappedKey, ulWrappedKeyLen, pTemplate, ulAttributeCount, phKey); if (rv == CKR_OK) spy_dump_ulong_out("hKey", *phKey); return retne(rv); } CK_RV C_DeriveKey(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hBaseKey, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount, CK_OBJECT_HANDLE_PTR phKey) { CK_RV rv; enter("C_DeriveKey"); spy_dump_ulong_in("hSession", hSession); spy_dump_mechanism_in("pMechanism", pMechanism); spy_dump_ulong_in("hBaseKey", hBaseKey); spy_attribute_list_in("pTemplate", pTemplate, ulAttributeCount); rv = po->C_DeriveKey(hSession, pMechanism, hBaseKey, pTemplate, ulAttributeCount, phKey); if (rv == CKR_OK) spy_dump_ulong_out("hKey", *phKey); return retne(rv); } CK_RV C_SeedRandom(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSeed, CK_ULONG ulSeedLen) { CK_RV rv; enter("C_SeedRandom"); spy_dump_ulong_in("hSession", hSession); spy_dump_string_in("pSeed[ulSeedLen]", pSeed, ulSeedLen); rv = po->C_SeedRandom(hSession, pSeed, ulSeedLen); return retne(rv); } CK_RV C_GenerateRandom(CK_SESSION_HANDLE hSession, CK_BYTE_PTR RandomData, CK_ULONG ulRandomLen) { CK_RV rv; enter("C_GenerateRandom"); spy_dump_ulong_in("hSession", hSession); rv = po->C_GenerateRandom(hSession, RandomData, ulRandomLen); if (rv == CKR_OK) spy_dump_string_out("RandomData[ulRandomLen]", RandomData, ulRandomLen); return retne(rv); } CK_RV C_GetFunctionStatus(CK_SESSION_HANDLE hSession) { CK_RV rv; enter("C_GetFunctionStatus"); spy_dump_ulong_in("hSession", hSession); rv = po->C_GetFunctionStatus(hSession); return retne(rv); } CK_RV C_CancelFunction(CK_SESSION_HANDLE hSession) { CK_RV rv; enter("C_CancelFunction"); spy_dump_ulong_in("hSession", hSession); rv = po->C_CancelFunction(hSession); return retne(rv); } CK_RV C_WaitForSlotEvent(CK_FLAGS flags, CK_SLOT_ID_PTR pSlot, CK_VOID_PTR pRserved) { CK_RV rv; 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(pInterface->pInterfaceName, "PKCS 11") != 0) { *retInterface = pInterface; return; } version = (CK_VERSION *)pInterface->pFunctionList; if (version->major == 2) { (*retInterface)->pFunctionList = pkcs11_spy; } else if (version->major == 3 && version->minor == 0) { (*retInterface)->pFunctionList = pkcs11_spy_3_0; } } 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) { 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) { 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) { 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); 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); 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); 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); 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); } OpenSC-0.26.1/src/pkcs11/pkcs11.exports000066400000000000000000000026701474147347300173730ustar00rootroot00000000000000C_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 OpenSC-0.26.1/src/pkcs11/pkcs11.h000066400000000000000000001713721474147347300161240ustar00rootroot00000000000000/* 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. */ #ifndef PKCS11_H #define PKCS11_H 1 #if defined(__cplusplus) 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 3 #define CRYPTOKI_VERSION_MINOR 0 #define CRYPTOKI_VERSION_REVISION 0 /* 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. */ #pragma pack(push, cryptoki, 1) #ifdef CRYPTOKI_EXPORTS #define CK_SPEC __declspec(dllexport) #else #define CK_SPEC __declspec(dllimport) #endif #else #define CK_SPEC #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_rsa_pkcs_mgf_type_t CK_RSA_PKCS_MGF_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_interface CK_INTERFACE #define ck_function_list _CK_FUNCTION_LIST #define ck_function_list_3_0 _CK_FUNCTION_LIST_3_0 #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_OTP_KEY (8UL) #define CKO_PROFILE (9UL) #define CKO_VENDOR_DEFINED (1UL << 31) #define CKP_INVALID_ID (0UL) #define CKP_BASELINE_PROVIDER (1UL) #define CKP_EXTENDED_PROVIDER (2UL) #define CKP_AUTHENTICATION_TOKEN (3UL) #define CKP_PUBLIC_CERTIFICATES_TOKEN (4UL) #define CKP_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_EC_EDWARDS (0x40UL) #define CKK_EC_MONTGOMERY (0x41UL) #define CKK_HKDF (0x42UL) #define CKK_VENDOR_DEFINED (1UL << 31) /* * 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) 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_UNIQUE_ID (4UL) #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_PUBLIC_KEY_INFO (0x129UL) #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_COPYABLE (0x171UL) #define CKA_DESTROYABLE (0x172UL) #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_OTP_FORMAT (0x220UL) #define CKA_OTP_LENGTH (0x221UL) #define CKA_OTP_TIME_INTERVAL (0x222UL) #define CKA_OTP_USER_FRIENDLY_MODE (0x223UL) #define CKA_OTP_CHALLENGE_REQUIREMENT (0x224UL) #define CKA_OTP_TIME_REQUIREMENT (0x225UL) #define CKA_OTP_COUNTER_REQUIREMENT (0x226UL) #define CKA_OTP_PIN_REQUIREMENT (0x227UL) #define CKA_OTP_USER_IDENTIFIER (0x22AUL) #define CKA_OTP_SERVICE_IDENTIFIER (0x22BUL) #define CKA_OTP_SERVICE_LOGO (0x22CUL) #define CKA_OTP_SERVICE_LOGO_TYPE (0x22DUL) #define CKA_OTP_COUNTER (0x22EUL) #define CKA_OTP_TIME (0x22FUL) #define CKA_ALLOWED_MECHANISMS (CKF_ARRAY_ATTRIBUTE | 0x600UL) #define CKA_PROFILE_ID (0x601UL) #define CKA_VENDOR_DEFINED (1UL << 31) 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_DSA_SHA224 (0x13UL) #define CKM_DSA_SHA256 (0x14UL) #define CKM_DSA_SHA384 (0x15UL) #define CKM_DSA_SHA512 (0x16UL) #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_SHA224_RSA_PKCS (0x46UL) #define CKM_SHA224_RSA_PKCS_PSS (0x47UL) #define CKM_SHA3_256_RSA_PKCS (0x60UL) #define CKM_SHA3_384_RSA_PKCS (0x61UL) #define CKM_SHA3_512_RSA_PKCS (0x62UL) #define CKM_SHA3_256_RSA_PKCS_PSS (0x63UL) #define CKM_SHA3_384_RSA_PKCS_PSS (0x64UL) #define CKM_SHA3_512_RSA_PKCS_PSS (0x65UL) #define CKM_SHA3_224_RSA_PKCS (0x66UL) #define CKM_SHA3_224_RSA_PKCS_PSS (0x67UL) #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_DES3_CMAC_GENERAL (0x137UL) #define CKM_DES3_CMAC (0x138UL) #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_SHA224 (0x255UL) #define CKM_SHA224_HMAC (0x256UL) #define CKM_SHA224_HMAC_GENERAL (0x257UL) #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_SHA3_256 (0x2B0UL) #define CKM_SHA3_256_HMAC (0x2B1UL) #define CKM_SHA3_256_HMAC_GENERAL (0x2B2UL) #define CKM_SHA3_256_KEY_GEN (0x2B3UL) #define CKM_SHA3_224 (0x2B5UL) #define CKM_SHA3_224_HMAC (0x2B6UL) #define CKM_SHA3_224_HMAC_GENERAL (0x2B7UL) #define CKM_SHA3_224_KEY_GEN (0x2B8UL) #define CKM_SHA3_384 (0x2C0UL) #define CKM_SHA3_384_HMAC (0x2C1UL) #define CKM_SHA3_384_HMAC_GENERAL (0x2C2UL) #define CKM_SHA3_384_KEY_GEN (0x2C3UL) #define CKM_SHA3_512 (0x2D0UL) #define CKM_SHA3_512_HMAC (0x2D1UL) #define CKM_SHA3_512_HMAC_GENERAL (0x2D2UL) #define CKM_SHA3_512_KEY_GEN (0x2D3UL) #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_ECDSA_SHA3_224 (0x1047UL) #define CKM_ECDSA_SHA3_256 (0x1048UL) #define CKM_ECDSA_SHA3_384 (0x1049UL) #define CKM_ECDSA_SHA3_512 (0x104AUL) #define CKM_ECDH1_DERIVE (0x1050UL) #define CKM_ECDH1_COFACTOR_DERIVE (0x1051UL) #define CKM_ECMQV_DERIVE (0x1052UL) #define CKM_EC_EDWARDS_KEY_PAIR_GEN (0x1055UL) #define CKM_EC_MONTGOMERY_KEY_PAIR_GEN (0x1056UL) #define CKM_EDDSA (0x1057UL) #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_AES_CMAC (0x108AUL) #define CKM_AES_CMAC_GENERAL (0x108BUL) #define CKM_AES_XCBC_MAC (0x108CUL) #define CKM_AES_XCBC_MAC_96 (0x108DUL) #define CKM_AES_GMAC (0x108EUL) #define CKM_BLOWFISH_KEY_GEN (0x1090UL) #define CKM_BLOWFISH_CBC (0x1091UL) #define CKM_TWOFISH_KEY_GEN (0x1092UL) #define CKM_TWOFISH_CBC (0x1093UL) #define CKM_DES_ECB_ENCRYPT_DATA (0x1100UL) #define CKM_DES_CBC_ENCRYPT_DATA (0x1101UL) #define CKM_DES3_ECB_ENCRYPT_DATA (0x1102UL) #define CKM_DES3_CBC_ENCRYPT_DATA (0x1103UL) #define CKM_AES_ECB_ENCRYPT_DATA (0x1104UL) #define CKM_AES_CBC_ENCRYPT_DATA (0x1105UL) #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_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 (0x1210UL) #define CKM_GOSTR3411_HMAC (0x1211UL) #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) #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_AES_OFB (0x2104UL) #define CKM_AES_CFB64 (0x2105UL) #define CKM_AES_CFB8 (0x2106UL) #define CKM_AES_CFB128 (0x2107UL) #define CKM_AES_CFB1 (0x2108UL) #define CKM_AES_KEY_WRAP (0x2109UL) #define CKM_AES_KEY_WRAP_PAD (0x210AUL) #define CKM_XEDDSA (0x4029UL) #define CKM_HKDF_DERIVE (0x402AUL) #define CKM_HKDF_DATA (0x402BUL) #define CKM_HKDF_KEY_GEN (0x402CUL) #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_MESSAGE_ENCRYPT (1UL << 1) #define CKF_MESSAGE_DECRYPT (1UL << 2) #define CKF_MESSAGE_SIGN (1UL << 3) #define CKF_MESSAGE_VERIFY (1UL << 4) #define CKF_MULTI_MESSAGE (1UL << 5) #define CKF_FIND_OBJECTS (1UL << 6) #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_OID (1UL << 23) #define CKF_EC_NAMEDCURVE CKF_EC_OID #define CKF_EC_UNCOMPRESS (1UL << 24) #define CKF_EC_COMPRESS (1UL << 25) #define CKF_EC_CURVENAME (1UL << 26) /* Flags for C_WaitForSlotEvent. */ #define CKF_DONT_BLOCK (1UL) /* Flags for Key derivation */ #define CKD_NULL (0x1UL) #define CKD_SHA1_KDF (0x2UL) #define CKD_SHA224_KDF (0x5UL) #define CKD_SHA256_KDF (0x6UL) #define CKD_SHA384_KDF (0x7UL) #define CKD_SHA512_KDF (0x8UL) 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 struct CK_ECMQV_DERIVE_PARAMS { unsigned long kdf; unsigned long ulSharedDataLen; unsigned char * pSharedData; unsigned long ulPublicDataLen; unsigned char * pPublicData; unsigned long ulPrivateDataLen; CK_OBJECT_HANDLE hPrivateData; unsigned long ulPublicDataLen2; unsigned char * pPublicData2; CK_OBJECT_HANDLE publicKey; } CK_ECMQV_DERIVE_PARAMS; typedef unsigned long ck_rsa_pkcs_mgf_type_t; typedef unsigned long CK_RSA_PKCS_OAEP_SOURCE_TYPE; typedef 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; unsigned long ulSourceDataLen; } CK_RSA_PKCS_OAEP_PARAMS; typedef struct CK_RSA_PKCS_PSS_PARAMS { ck_mechanism_type_t hashAlg; CK_RSA_PKCS_MGF_TYPE mgf; unsigned long sLen; } CK_RSA_PKCS_PSS_PARAMS; #define CKG_MGF1_SHA1 (0x00000001UL) #define CKG_MGF1_SHA224 (0x00000005UL) #define CKG_MGF1_SHA256 (0x00000002UL) #define CKG_MGF1_SHA384 (0x00000003UL) #define CKG_MGF1_SHA512 (0x00000004UL) #define CKG_MGF1_SHA3_224 (0x00000006UL) #define CKG_MGF1_SHA3_256 (0x00000007UL) #define CKG_MGF1_SHA3_384 (0x00000008UL) #define CKG_MGF1_SHA3_512 (0x00000009UL) #define CKZ_DATA_SPECIFIED (0x00000001UL) typedef struct CK_GCM_PARAMS { void * pIv; unsigned long ulIvLen; unsigned long ulIvBits; void * pAAD; unsigned long ulAADLen; unsigned long ulTagBits; } CK_GCM_PARAMS; typedef struct CK_CCM_PARAMS { unsigned long ulDataLen; unsigned char *pNonce; unsigned long ulNonceLen; unsigned char *pAAD; unsigned long ulAADLen; unsigned long ulMACLen; } CK_CCM_PARAMS; /* EDDSA */ typedef struct CK_EDDSA_PARAMS { unsigned char phFlag; unsigned long ulContextDataLen; unsigned char *pContextData; } CK_EDDSA_PARAMS; typedef CK_EDDSA_PARAMS *CK_EDDSA_PARAMS_PTR; /* XEDDSA */ typedef struct CK_XEDDSA_PARAMS { unsigned long hash; } CK_XEDDSA_PARAMS; typedef CK_XEDDSA_PARAMS *CK_XEDDSA_PARAMS_PTR; typedef struct CK_AES_CTR_PARAMS { unsigned long ulCounterBits; unsigned char cb[16]; } CK_AES_CTR_PARAMS; typedef CK_AES_CTR_PARAMS *CK_AES_CTR_PARAMS_PTR; typedef unsigned long CK_MAC_GENERAL_PARAMS; typedef CK_MAC_GENERAL_PARAMS *CK_MAC_GENERAL_PARAMS_PTR; typedef unsigned long ck_rv_t; typedef ck_rv_t (*ck_notify_t) (ck_session_handle_t session, ck_notification_t event, void *application); struct ck_interface { char * pInterfaceName; void * pFunctionList; ck_flags_t flags; }; #define CKF_INTERFACE_FORK_SAFE (0x00000001UL) /* Forward reference. */ struct ck_function_list; struct ck_function_list_3_0; #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 authentication_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)); _CK_DECLARE_FUNCTION (C_GetInterfaceList, (struct ck_interface *interfaces_list, unsigned long *count)); _CK_DECLARE_FUNCTION (C_GetInterface, (unsigned char *interface_name, struct ck_version *version, struct ck_interface **interface_ptr, ck_flags_t flags)); _CK_DECLARE_FUNCTION (C_LoginUser, (ck_session_handle_t session, ck_user_type_t user_type, unsigned char *pin, unsigned long pin_len, unsigned char *username, unsigned long username_len)); _CK_DECLARE_FUNCTION (C_SessionCancel, (ck_session_handle_t session, ck_flags_t flags)); _CK_DECLARE_FUNCTION (C_MessageEncryptInit, (ck_session_handle_t session, struct ck_mechanism *mechanism, ck_object_handle_t key)); _CK_DECLARE_FUNCTION (C_EncryptMessage, (ck_session_handle_t session, void *parameter, unsigned long parameter_len, unsigned char *associated_data, unsigned long associated_data_len, unsigned char *plaintext, unsigned long plaintext_len, unsigned char *ciphertext, unsigned long *ciphertext_len)); _CK_DECLARE_FUNCTION (C_EncryptMessageBegin, (ck_session_handle_t session, void *parameter, unsigned long parameter_len, unsigned char *associated_data, unsigned long associated_data_len)); _CK_DECLARE_FUNCTION (C_EncryptMessageNext, (ck_session_handle_t session, void *parameter, unsigned long parameter_len, unsigned char *plaintext_part, unsigned long plaintext_part_len, unsigned char *ciphertext_part, unsigned long *ciphertext_part_len, ck_flags_t flags)); _CK_DECLARE_FUNCTION (C_MessageEncryptFinal, (ck_session_handle_t session)); _CK_DECLARE_FUNCTION (C_MessageDecryptInit, (ck_session_handle_t session, struct ck_mechanism *mechanism, ck_object_handle_t key)); _CK_DECLARE_FUNCTION (C_DecryptMessage, (ck_session_handle_t session, void *parameter, unsigned long parameter_len, unsigned char *associated_data, unsigned long associated_data_len, unsigned char *ciphertext, unsigned long ciphertext_len, unsigned char *plaintext, unsigned long *plaintext_len)); _CK_DECLARE_FUNCTION (C_DecryptMessageBegin, (ck_session_handle_t session, void *parameter, unsigned long parameter_len, unsigned char *associated_data, unsigned long associated_data_len)); _CK_DECLARE_FUNCTION (C_DecryptMessageNext, (ck_session_handle_t session, void *parameter, unsigned long parameter_len, unsigned char *ciphertext_part, unsigned long ciphertext_part_len, unsigned char *plaintext_part, unsigned long *plaintext_part_len, ck_flags_t flags)); _CK_DECLARE_FUNCTION (C_MessageDecryptFinal, (ck_session_handle_t session)); _CK_DECLARE_FUNCTION (C_MessageSignInit, (ck_session_handle_t session, struct ck_mechanism *mechanism, ck_object_handle_t key)); _CK_DECLARE_FUNCTION (C_SignMessage, (ck_session_handle_t session, void *parameter, unsigned long parameter_len, unsigned char *data, unsigned long data_len, unsigned char *signature, unsigned long *signature_len)); _CK_DECLARE_FUNCTION (C_SignMessageBegin, (ck_session_handle_t session, void *parameter, unsigned long parameter_len)); _CK_DECLARE_FUNCTION (C_SignMessageNext, (ck_session_handle_t session, void *parameter, unsigned long parameter_len, unsigned char *data, unsigned long data_len, unsigned char *signature, unsigned long *signature_len)); _CK_DECLARE_FUNCTION (C_MessageSignFinal, (ck_session_handle_t session)); _CK_DECLARE_FUNCTION (C_MessageVerifyInit, (ck_session_handle_t session, struct ck_mechanism *mechanism, ck_object_handle_t key)); _CK_DECLARE_FUNCTION (C_VerifyMessage, (ck_session_handle_t session, void *parameter, unsigned long parameter_len, unsigned char *data, unsigned long data_len, unsigned char *signature, unsigned long signature_len)); _CK_DECLARE_FUNCTION (C_VerifyMessageBegin, (ck_session_handle_t session, void *parameter, unsigned long parameter_len)); _CK_DECLARE_FUNCTION (C_VerifyMessageNext, (ck_session_handle_t session, void *parameter, unsigned long parameter_len, unsigned char *data, unsigned long data_len, unsigned char *signature, unsigned long signature_len)); _CK_DECLARE_FUNCTION (C_MessageVerifyFinal, (ck_session_handle_t session)); /* Flags in Message-based encryption/decryption API */ #define CKF_END_OF_MESSAGE (0x00000001UL) 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; }; struct ck_function_list_3_0 { 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; /* PKCS #11 3.0 functions */ 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; }; 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_ACTION_PROHIBITED (0x1BUL) #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_CURVE_NOT_SUPPORTED (0x140UL) #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 #endif #ifndef TRUE #define TRUE 1 #endif #endif typedef struct CK_HKDF_PARAMS { CK_BBOOL bExtract; CK_BBOOL bExpand; CK_MECHANISM_TYPE prfHashMechanism; CK_ULONG ulSaltType; CK_BYTE_PTR pSalt; CK_ULONG ulSaltLen; CK_OBJECT_HANDLE hSaltKey; CK_BYTE_PTR pInfo; CK_ULONG ulInfoLen; } CK_HKDF_PARAMS; #define CKF_HKDF_SALT_NULL 0x00000001UL #define CKF_HKDF_SALT_DATA 0x00000002UL #define CKF_HKDF_SALT_KEY 0x00000004UL 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 ck_rsa_pkcs_mgf_type_t *CK_RSA_PKCS_MGF_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_interface CK_INTERFACE; typedef struct ck_interface *CK_INTERFACE_PTR; typedef struct ck_interface **CK_INTERFACE_PTR_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_function_list_3_0 CK_FUNCTION_LIST_3_0; typedef struct ck_function_list_3_0 *CK_FUNCTION_LIST_3_0_PTR; typedef struct ck_function_list_3_0 **CK_FUNCTION_LIST_3_0_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_rsa_pkcs_mgf_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_interface #undef ck_function_list #undef ck_function_list_3_0 #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 */ /* System dependencies. */ #if defined(_WIN32) || defined(CRYPTOKI_FORCE_WIN32) #pragma pack(pop, cryptoki) #endif #if defined(__cplusplus) } #endif #endif /* PKCS11_H */ OpenSC-0.26.1/src/pkcs11/sc-pkcs11.h000066400000000000000000000465121474147347300165240ustar00rootroot00000000000000/* * sc-pkcs11.h: OpenSC project's PKCS#11 implementation header * * Copyright (C) 2002 Timo Teräs * * 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_pkcs11_h__ #define __sc_pkcs11_h__ #include "config.h" #include "libopensc/opensc.h" #include "libopensc/pkcs15.h" #include "libopensc/log.h" #define CRYPTOKI_EXPORTS #include "pkcs11.h" #include "pkcs11-opensc.h" #include "pkcs11-display.h" #ifdef __cplusplus extern "C" { #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 #define SC_PKCS11_PIN_UNBLOCK_SO_LOGGED_INITPIN 3 #define SC_PKCS11_SLOT_FOR_PIN_USER 1 #define SC_PKCS11_SLOT_FOR_PIN_SIGN 2 #define SC_PKCS11_SLOT_CREATE_ALL 8 #define SC_PKCS11_SLOT_FOR_PINS (SC_PKCS11_SLOT_FOR_PIN_USER | SC_PKCS11_SLOT_FOR_PIN_SIGN) #ifdef __cplusplus } #endif /* Decide whether to use pkcs11 for initialization support */ #ifdef ENABLE_OPENSSL #define USE_PKCS15_INIT #endif #ifdef __cplusplus extern "C" { #endif struct sc_pkcs11_session; struct sc_pkcs11_slot; struct sc_pkcs11_card; struct sc_pkcs11_config { unsigned int max_virtual_slots; unsigned int slots_per_card; unsigned char lock_login; unsigned char atomic; unsigned char init_sloppy; unsigned int pin_unblock_style; unsigned int create_puk_slot; unsigned int create_slots_flags; unsigned char ignore_pin_length; }; /* * PKCS#11 Object abstraction layer */ struct sc_pkcs11_object_ops { /* Generic operations */ void (*release)(void *); /* 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); 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 *); /* Cryptographic methods */ CK_RV (*sign)(struct sc_pkcs11_session *, void *, CK_MECHANISM_PTR, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulDataLen); CK_RV (*unwrap_key)(struct sc_pkcs11_session *, void *, CK_MECHANISM_PTR, CK_BYTE_PTR pData, CK_ULONG ulDataLen, 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, CK_BYTE_PTR pDerived, CK_ULONG_PTR pulDerivedLen); /* 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 */ }; struct sc_pkcs11_object { CK_OBJECT_HANDLE handle; int flags; struct sc_pkcs11_object_ops *ops; }; #define SC_PKCS11_OBJECT_SEEN 0x0001 #define SC_PKCS11_OBJECT_HIDDEN 0x0002 #define SC_PKCS11_OBJECT_RECURS 0x8000 /* * PKCS#11 smart card Framework abstraction */ struct sc_pkcs11_framework_ops { /* Detect and bind card to framework */ CK_RV (*bind)(struct sc_pkcs11_card *, struct sc_app_info *); /* Unbind and release allocated resources */ CK_RV (*unbind)(struct sc_pkcs11_card *); /* Create tokens to virtual slots and * objects in tokens; called after bind */ CK_RV (*create_tokens)(struct sc_pkcs11_card *, struct sc_app_info *); CK_RV (*release_token)(struct sc_pkcs11_card *, void *); /* Login and logout */ CK_RV (*login)(struct sc_pkcs11_slot *, CK_USER_TYPE, CK_CHAR_PTR, CK_ULONG); CK_RV (*logout)(struct sc_pkcs11_slot *); CK_RV (*change_pin)(struct sc_pkcs11_slot *, CK_CHAR_PTR, CK_ULONG, CK_CHAR_PTR, CK_ULONG); /* * 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, CK_UTF8CHAR_PTR); CK_RV (*init_pin)(struct sc_pkcs11_slot *, CK_UTF8CHAR_PTR, CK_ULONG); CK_RV (*create_object)(struct sc_pkcs11_slot *, CK_ATTRIBUTE_PTR, CK_ULONG, CK_OBJECT_HANDLE_PTR); CK_RV (*gen_keypair)(struct sc_pkcs11_slot *, CK_MECHANISM_PTR, CK_ATTRIBUTE_PTR, CK_ULONG, CK_ATTRIBUTE_PTR, CK_ULONG, CK_OBJECT_HANDLE_PTR, CK_OBJECT_HANDLE_PTR); CK_RV (*get_random)(struct sc_pkcs11_slot *, CK_BYTE_PTR, CK_ULONG); }; /* * PKCS#11 Slot (used to access card with specific framework data) */ #ifndef _WIN32 typedef unsigned long long sc_timestamp_t; #else typedef unsigned __int64 sc_timestamp_t; #endif #define SC_PKCS11_FRAMEWORK_DATA_MAX_NUM 4 struct sc_pkcs11_card { sc_reader_t *reader; sc_card_t *card; struct sc_pkcs11_framework_ops *framework; void *fws_data[SC_PKCS11_FRAMEWORK_DATA_MAX_NUM]; /* 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 * slot alive. PKCS#11 2.30 allows allows adding but not removing slots until * the application calls `C_GetSlotList` with `NULL`. This flag tracks the * visibility to the application */ #define SC_PKCS11_SLOT_FLAG_SEEN 1 struct sc_pkcs11_slot { CK_SLOT_ID id; /* ID of the slot */ int login_user; /* Currently logged in user */ 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 */ 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 */ list_t objects; /* Objects in this slot */ unsigned int nsessions; /* Number of sessions using this slot */ sc_timestamp_t slot_state_expires; int fw_data_idx; /* Index of framework data */ 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; enum { SC_PKCS11_OPERATION_FIND = 0, SC_PKCS11_OPERATION_SIGN, 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 */ int key_types[MAX_KEY_TYPES]; /* for sign/decipher ops */ unsigned int obj_size; /* General management */ void (*release)(sc_pkcs11_operation_t *); /* Digest/sign Operations */ CK_RV (*md_init)(sc_pkcs11_operation_t *); CK_RV (*md_update)(sc_pkcs11_operation_t *, CK_BYTE_PTR, CK_ULONG); CK_RV (*md_final)(sc_pkcs11_operation_t *, CK_BYTE_PTR, CK_ULONG_PTR); CK_RV (*sign_init)(sc_pkcs11_operation_t *, struct sc_pkcs11_object *); CK_RV (*sign_update)(sc_pkcs11_operation_t *, CK_BYTE_PTR, CK_ULONG); CK_RV (*sign_final)(sc_pkcs11_operation_t *, CK_BYTE_PTR, CK_ULONG_PTR); CK_RV (*sign_size)(sc_pkcs11_operation_t *, CK_ULONG_PTR); CK_RV (*verif_init)(sc_pkcs11_operation_t *, struct sc_pkcs11_object *); CK_RV (*verif_update)(sc_pkcs11_operation_t *, CK_BYTE_PTR, CK_ULONG); CK_RV (*verif_final)(sc_pkcs11_operation_t *, 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; /* * Generic operation */ 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; }; /* Find Operation */ #define SC_PKCS11_FIND_INC_HANDLES 32 struct sc_pkcs11_find_operation { struct sc_pkcs11_operation operation; int num_handles, current_handle, allocated_handles; CK_OBJECT_HANDLE *handles; }; /* * PKCS#11 Session */ struct sc_pkcs11_session { CK_SESSION_HANDLE handle; /* Session to this slot */ struct sc_pkcs11_slot *slot; CK_FLAGS flags; /* Notifications */ CK_NOTIFY notify_callback; CK_VOID_PTR notify_data; /* Active operations - one per type */ struct sc_pkcs11_operation *operation[SC_PKCS11_OPERATION_MAX]; }; typedef struct sc_pkcs11_session sc_pkcs11_session_t; /* Module variables */ extern struct sc_context *context; extern struct sc_pkcs11_config sc_pkcs11_conf; extern list_t sessions; extern list_t virtual_slots; extern list_t cards; /* Framework definitions */ extern struct sc_pkcs11_framework_ops framework_pkcs15; extern struct sc_pkcs11_framework_ops framework_pkcs15init; 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, 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); void init_slot_info(CK_SLOT_INFO_PTR pInfo, sc_reader_t *reader); CK_RV card_detect(sc_reader_t *reader); 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); CK_RV reset_login_state(struct sc_pkcs11_slot *slot, CK_RV rv); CK_RV push_login_state(struct sc_pkcs11_slot *slot, CK_USER_TYPE userType, CK_CHAR_PTR pPin, CK_ULONG ulPinLen); void pop_login_state(struct sc_pkcs11_slot *slot); void pop_all_login_states(struct sc_pkcs11_slot *slot); /* Session manipulation */ CK_RV get_session(CK_SESSION_HANDLE hSession, struct sc_pkcs11_session ** session); CK_RV session_start_operation(struct sc_pkcs11_session *, int, sc_pkcs11_mechanism_type_t *, struct sc_pkcs11_operation **); CK_RV session_get_operation(struct sc_pkcs11_session *, int, struct sc_pkcs11_operation **); CK_RV session_stop_operation(struct sc_pkcs11_session *, int); CK_RV sc_pkcs11_close_all_sessions(CK_SLOT_ID); /* Generic secret key stuff */ CK_RV sc_pkcs11_create_secret_key(struct sc_pkcs11_session *, const u8 *, size_t, CK_ATTRIBUTE_PTR, CK_ULONG, struct sc_pkcs11_object **); /* Generic object handling */ CK_RV sc_pkcs11_any_cmp_attribute(struct sc_pkcs11_session *, void *, CK_ATTRIBUTE_PTR); /* Get attributes from template (misc.c) */ CK_RV attr_find(CK_ATTRIBUTE_PTR, CK_ULONG, CK_ULONG, void *, size_t *); CK_RV attr_find2(CK_ATTRIBUTE_PTR, CK_ULONG, CK_ATTRIBUTE_PTR, CK_ULONG, CK_ULONG, void *, size_t *); CK_RV attr_find_ptr(CK_ATTRIBUTE_PTR, CK_ULONG, CK_ULONG, void **, size_t *); CK_RV attr_find_ptr2(CK_ATTRIBUTE_PTR pTemp1, CK_ULONG ulCount1, CK_ATTRIBUTE_PTR pTemp2, CK_ULONG ulCount2, CK_ULONG type, void **ptr, size_t * sizep); CK_RV attr_find_and_allocate_ptr(CK_ATTRIBUTE_PTR, CK_ULONG, CK_ULONG, void **, size_t *); CK_RV attr_find_var(CK_ATTRIBUTE_PTR, CK_ULONG, CK_ULONG, void *, size_t *); 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 **); 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, CK_MECHANISM_INFO_PTR); 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_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_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_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, 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 *), 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 **); CK_RV sc_pkcs11_register_generic_mechanisms(struct sc_pkcs11_card *); #ifdef ENABLE_OPENSSL void sc_pkcs11_register_openssl_mechanisms(struct sc_pkcs11_card *); #endif CK_RV sc_pkcs11_register_sign_and_hash_mechanism(struct sc_pkcs11_card *, CK_MECHANISM_TYPE, CK_MECHANISM_TYPE, sc_pkcs11_mechanism_type_t *); #ifdef ENABLE_OPENSSL 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 */ void load_pkcs11_parameters(struct sc_pkcs11_config *, struct sc_context *); /* Locking primitives at the pkcs11 level */ CK_RV sc_pkcs11_init_lock(CK_C_INITIALIZE_ARGS_PTR); CK_RV sc_pkcs11_lock(void); void sc_pkcs11_unlock(void); void sc_pkcs11_free_lock(void); #ifdef __cplusplus } #endif #endif /* __sc_pkcs11_h__ */ OpenSC-0.26.1/src/pkcs11/slot.c000066400000000000000000000405611474147347300157710ustar00rootroot00000000000000/* * slot.c: reader, smart card and slot related management functions * * Copyright (C) 2002 Timo Teräs * Copyright (C) 2009 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 */ #include "config.h" #include "libopensc/opensc.h" #include #include #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 /* This should be the last framework, because it * will assume the card is blank and try to initialize it */ &framework_pkcs15init, #endif NULL }; 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 == 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; } void init_slot_info(CK_SLOT_INFO_PTR pInfo, sc_reader_t *reader) { if (reader) { strcpy_bp(pInfo->slotDescription, reader->name, 64); strcpy_bp(pInfo->manufacturerID, reader->vendor, 32); pInfo->hardwareVersion.major = reader->version_major; pInfo->hardwareVersion.minor = reader->version_minor; } else { strcpy_bp(pInfo->slotDescription, "Virtual hotplug slot", 64); strcpy_bp(pInfo->manufacturerID, OPENSC_VS_FF_COMPANY_NAME, 32); pInfo->hardwareVersion.major = OPENSC_VERSION_MAJOR; pInfo->hardwareVersion.minor = OPENSC_VERSION_MINOR; } pInfo->flags = CKF_REMOVABLE_DEVICE | CKF_HW_SLOT; pInfo->firmwareVersion.major = 0; pInfo->firmwareVersion.minor = 0; } /* simclist helpers to locate interesting objects by ID */ static int object_list_seeker(const void *el, const void *key) { const struct sc_pkcs11_object *object = (struct sc_pkcs11_object *)el; if ((el == NULL) || (key == NULL)) return 0; if (object->handle == *(CK_OBJECT_HANDLE*)key) return 1; return 0; } CK_RV create_slot(sc_reader_t *reader) { /* 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; slot = (struct sc_pkcs11_slot *)calloc(1, sizeof(struct sc_pkcs11_slot)); if (!slot) return CKR_HOST_MEMORY; list_append(&virtual_slots, slot); if (0 != list_init(&slot->objects)) { return CKR_HOST_MEMORY; } list_attributes_seeker(&slot->objects, object_list_seeker); if (0 != list_init(&slot->logins)) { 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; memset(slot, 0, sizeof *slot); slot->logins = logins; slot->objects = objects; } slot->login_user = -1; slot->id = (CK_SLOT_ID) list_locate(&virtual_slots, slot); init_slot_info(&slot->slot_info, reader); slot->reader = reader; DEBUG_VSS(slot, "Finished initializing this slot"); return CKR_OK; } void sc_pkcs11_card_free(struct sc_pkcs11_card *p11card) { 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); } free(p11card->mechanisms[i]); } free(p11card->mechanisms); free(p11card); } } CK_RV card_removed(sc_reader_t * reader) { unsigned int i; struct sc_pkcs11_card *p11card = NULL; /* Mark all slots as "token not present" */ sc_log(context, "%s: card removed", reader->name); 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->reader == reader) { /* Save the "card" object */ if (slot->p11card) p11card = slot->p11card; slot_token_removed(slot->id); } } sc_pkcs11_card_free(p11card); return CKR_OK; } CK_RV card_detect(sc_reader_t *reader) { struct sc_pkcs11_card *p11card = NULL; int free_p11card = 0; int rc; CK_RV rv; unsigned int i; int j; sc_log(context, "%s: Detecting smart card", reader->name); /* Check if someone inserted a card */ again: rc = sc_detect_card_presence(reader); if (rc < 0) { sc_log(context, "%s: failed, %s", reader->name, sc_strerror(rc)); return sc_to_cryptoki_error(rc, NULL); } if (rc == 0) { sc_log(context, "%s: card absent", reader->name); card_removed(reader); /* Release all resources */ return CKR_TOKEN_NOT_PRESENT; } /* 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; */ card_removed(reader); goto again; } /* Locate a slot related to the reader */ for (i=0; ireader == reader) { p11card = slot->p11card; break; } } /* Detect the card if it's not known already */ if (p11card == NULL) { sc_log(context, "%s: First seen the card ", reader->name); 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) { sc_log(context, "%s: SC connect card error %i", reader->name, rc); rv = sc_to_cryptoki_error(rc, NULL); goto fail; } /* escape commands are only guaranteed to be working with a card * inserted. That's why by now, after sc_connect_card() the reader's * metadata may have changed. We re-initialize the metadata for every * slot of this reader here. */ if (reader->flags & SC_READER_ENABLE_ESCAPE) { for (i = 0; ireader == reader) init_slot_info(&slot->slot_info, reader); } } sc_log(context, "%s: Connected SC card %p", reader->name, p11card->card); } /* Detect the framework */ if (p11card->framework == NULL) { struct sc_app_info *app_generic = sc_pkcs15_get_application_by_type(p11card->card, "generic"); sc_log(context, "%s: Detecting Framework. %i on-card applications", reader->name, p11card->card->app_count); sc_log(context, "%s: generic application %s", reader->name, app_generic ? app_generic->label : ""); for (i = 0; frameworks[i]; i++) if (frameworks[i]->bind != NULL) break; /*TODO: only first framework is used: pkcs15init framework is not reachable here */ if (frameworks[i] == NULL) { rv = CKR_GENERAL_ERROR; goto fail; } p11card->framework = frameworks[i]; /* Initialize framework */ 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 *conf_block = NULL; int 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); if (rv == CKR_TOKEN_NOT_RECOGNIZED && enable_InitToken) { sc_log(context, "%s: 'InitToken' enabled -- accept non-binded card", reader->name); rv = CKR_OK; } if (rv != CKR_OK) { sc_log(context, "%s: cannot bind 'generic' token: rv 0x%lX", reader->name, rv); goto fail; } sc_log(context, "%s: Creating 'generic' token.", reader->name); rv = frameworks[i]->create_tokens(p11card, app_generic); if (rv != CKR_OK) { sc_log(context, "%s: create 'generic' token error 0x%lX", reader->name, rv); goto fail; } /* p11card is now bound to some slot */ free_p11card = 0; } /* Now bind the rest of applications that are not 'generic' */ for (j = 0; j < p11card->card->app_count; j++) { struct sc_app_info *app_info = p11card->card->app[j]; char *app_name = app_info ? app_info->label : ""; if (app_generic && app_generic == p11card->card->app[j]) continue; sc_log(context, "%s: Binding %s token.", reader->name, app_name); rv = frameworks[i]->bind(p11card, app_info); if (rv != CKR_OK) { sc_log(context, "%s: bind %s token error Ox%lX", reader->name, app_name, rv); continue; } sc_log(context, "%s: Creating %s token.", reader->name, app_name); rv = frameworks[i]->create_tokens(p11card, app_info); if (rv != CKR_OK) { sc_log(context, "%s: create %s token error 0x%lX", reader->name, app_name, rv); goto fail; } /* p11card is now bound to some slot */ free_p11card = 0; } } sc_log(context, "%s: Detection ended", reader->name); rv = CKR_OK; fail: if (free_p11card) { sc_pkcs11_card_free(p11card); } return rv; } CK_RV card_detect_all(void) { 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) { card_removed(reader); /* 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; } } } else { /* Locate a slot related to the reader */ int found = 0; for (j = 0; jreader == reader) { found = 1; 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); } } sc_log(context, "All cards detected"); return CKR_OK; } /* Allocates an existing slot to a card */ CK_RV slot_allocate(struct sc_pkcs11_slot ** slot, struct sc_pkcs11_card * p11card) { unsigned int i; struct sc_pkcs11_slot *tmp_slot = NULL; /* Locate a free slot for this reader */ for (i=0; i< list_size(&virtual_slots); i++) { tmp_slot = (struct sc_pkcs11_slot *)list_get_at(&virtual_slots, i); if (tmp_slot->reader == p11card->reader && tmp_slot->p11card == NULL) break; } if (!tmp_slot || (i == list_size(&virtual_slots))) return CKR_FUNCTION_FAILED; sc_log(context, "Allocated slot 0x%lx for card in reader %s", tmp_slot->id, p11card->reader->name); tmp_slot->p11card = p11card; tmp_slot->events = SC_EVENT_CARD_INSERTED; *slot = tmp_slot; return CKR_OK; } CK_RV slot_get_slot(CK_SLOT_ID id, struct sc_pkcs11_slot ** slot) { if (context == NULL) return CKR_CRYPTOKI_NOT_INITIALIZED; *slot = list_seek(&virtual_slots, &id); /* FIXME: check for null? */ if (!*slot) return CKR_SLOT_ID_INVALID; return CKR_OK; } CK_RV slot_get_token(CK_SLOT_ID id, struct sc_pkcs11_slot ** slot) { CK_RV rv; sc_log(context, "Slot(id=0x%lX): get token", id); rv = slot_get_slot(id, slot); if (rv != CKR_OK) return rv; if (!((*slot)->slot_info.flags & CKF_TOKEN_PRESENT)) { 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); if (rv != CKR_OK) return rv; } if (!((*slot)->slot_info.flags & CKF_TOKEN_PRESENT)) { sc_log(context, "card detected, but slot not presenting token"); return CKR_TOKEN_NOT_PRESENT; } sc_log(context, "Slot-get-token returns OK"); return CKR_OK; } CK_RV slot_token_removed(CK_SLOT_ID id) { CK_RV rv; int token_was_present; struct sc_pkcs11_slot *slot; struct sc_pkcs11_object *object; sc_log(context, "slot_token_removed(0x%lx)", id); rv = slot_get_slot(id, &slot); if (rv != CKR_OK) return rv; token_was_present = (slot->slot_info.flags & CKF_TOKEN_PRESENT); /* Terminate active sessions */ sc_pkcs11_close_all_sessions(id); while ((object = list_fetch(&slot->objects))) { if (object->ops->release) object->ops->release(object); } /* Release framework stuff */ if (slot->p11card != 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; } slot->p11card = NULL; } /* Reset relevant slot properties */ slot->slot_info.flags &= ~CKF_TOKEN_PRESENT; slot->login_user = -1; pop_all_login_states(slot); if (token_was_present) slot->events = SC_EVENT_CARD_REMOVED; memset(&slot->token_info, 0, sizeof slot->token_info); return CKR_OK; } /* Called from C_WaitForSlotEvent */ CK_RV slot_find_changed(CK_SLOT_ID_PTR idp, int mask) { unsigned int i; LOG_FUNC_CALLED(context); card_detect_all(); for (i=0; iid, (slot->slot_info.flags & CKF_TOKEN_PRESENT), slot->events); if ((slot->events & SC_EVENT_CARD_INSERTED) && !(slot->slot_info.flags & CKF_TOKEN_PRESENT)) { /* If a token has not been initialized, clear the inserted event */ slot->events &= ~SC_EVENT_CARD_INSERTED; } sc_log(context, "mask: 0x%02X events: 0x%02X result: %d", mask, slot->events, (slot->events & mask)); if (slot->events & mask) { slot->events &= ~mask; *idp = slot->id; LOG_FUNC_RETURN(context, CKR_OK); } } LOG_FUNC_RETURN(context, CKR_NO_EVENT); } OpenSC-0.26.1/src/pkcs11/versioninfo-pkcs11-spy.rc.in000066400000000000000000000022431474147347300220440ustar00rootroot00000000000000#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 OpenSC-0.26.1/src/pkcs11/versioninfo-pkcs11.rc.in000066400000000000000000000022371474147347300212360ustar00rootroot00000000000000#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 OpenSC-0.26.1/src/pkcs15init/000077500000000000000000000000001474147347300155265ustar00rootroot00000000000000OpenSC-0.26.1/src/pkcs15init/Makefile.am000066400000000000000000000024731474147347300175700ustar00rootroot00000000000000include $(top_srcdir)/win32/ltrc.inc MAINTAINERCLEANFILES = $(srcdir)/Makefile.in EXTRA_DIST = Makefile.mak noinst_LTLIBRARIES = libpkcs15init.la noinst_HEADERS = profile.h pkcs15-init.h pkcs15-oberthur.h pkcs15-iasecc.h dist_pkgdata_DATA = \ cyberflex.profile \ flex.profile \ cardos.profile \ oberthur.profile \ starcos.profile \ setcos.profile \ pkcs15.profile \ muscle.profile \ rutoken.profile \ asepcos.profile \ entersafe.profile \ epass2003.profile \ rutoken_ecp.profile \ rutoken_lite.profile \ myeid.profile \ authentic.profile \ iasecc.profile \ ias_adele_admin1.profile ias_adele_admin2.profile ias_adele_common.profile \ iasecc_generic_pki.profile iasecc_admin_eid.profile iasecc_generic_oberthur.profile \ openpgp.profile sc-hsm.profile \ isoApplet.profile gids.profile 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-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 \ pkcs15-oberthur.c pkcs15-oberthur-awp.c \ pkcs15-authentic.c pkcs15-iasecc.c pkcs15-openpgp.c \ pkcs15-sc-hsm.c \ pkcs15-isoApplet.c pkcs15-gids.c OpenSC-0.26.1/src/pkcs15init/Makefile.mak000066400000000000000000000012351474147347300177360ustar00rootroot00000000000000TOPDIR = ..\.. TARGET = pkcs15init.lib OBJECTS = pkcs15-lib.obj profile.obj \ pkcs15-cflex.obj \ pkcs15-cardos.obj pkcs15-starcos.obj \ pkcs15-oberthur.obj pkcs15-oberthur-awp.obj \ pkcs15-setcos.obj \ pkcs15-muscle.obj pkcs15-asepcos.obj pkcs15-rutoken.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 all: $(TARGET) !INCLUDE $(TOPDIR)\win32\Make.rules.mak $(TARGET): $(OBJECTS) lib $(LIBFLAGS) /out:$(TARGET) $(OBJECTS) OpenSC-0.26.1/src/pkcs15init/README000066400000000000000000000010741474147347300164100ustar00rootroot00000000000000 Very brief instructions To init card: Erase card and create pkcs15 dir ./pkcs15-init -EC Store a PIN on the card, using ID 01 ./pkcs15-init -P --auth-id 01 --pin aaaa --puk bbbb --label "My PIN" Generate a 512 bit RSA key and store on card, protected by the above PIN ./pkcs15-init -G rsa/512 --auth-id 01 Or, store a pkcs12 key/certificate pair ./pkcs15-init --auth-id 01 -f pkcs12 -S mycert.p12 Note that the pkcs12 file must use just one password - if you use different passwords for integrity and confidentiality, OpenSC will fail to import the keys. OpenSC-0.26.1/src/pkcs15init/asepcos.profile000066400000000000000000000050441474147347300205500ustar00rootroot00000000000000# # PKCS15 r/w profile for Athena APCOS cards # cardinfo { max-pin-length = 16; pin-encoding = ascii-numeric; pin-pad-char = 0x00; } # Default settings. # This option block will always be processed. option default { macros { so-pin-flags = local, initialized, soPin; df_acl = *=$SOPIN; } } # This option sets up the card so that a single # user PIN protects all files option onepin { macros { so-pin-flags = local, initialized; df_acl = *=$PIN; } } # Define reasonable limits for PINs and PUK PIN so-pin { reference = 1; flags = $so-pin-flags; } PIN so-puk { reference = 2; } PIN user-pin { attempts = 3; flags = local, initialized; } PIN user-puk { attempts = 10; flags = local, initialized; } # Additional filesystem info. # This is added to the file system info specified in the # main profile. filesystem { DF MF { ACL = *=AUT0; DF PKCS15-AppDF { size = 0; ACL = $df_acl; EF PKCS15-PrKDF { size = 384; } EF PKCS15-PuKDF { size = 384; } # 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 { # This is a dummy entry - pkcs15-init insists that # this is present EF private-key { file-id = 0100; ACL = *=NEVER, CRYPTO=$PIN, UPDATE=$PIN; } # public keys 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; } # 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; } } } } } OpenSC-0.26.1/src/pkcs15init/authentic.profile000066400000000000000000000055551474147347300211060ustar00rootroot00000000000000# # PKCS15 r/w profile for Oberthur AuthentIC v3 cards # cardinfo { label = "AuthentIC.v3"; manufacturer = "Oberthur COSMO.v7"; max-pin-length = 63; min-pin-length = 4; pin-encoding = ascii-numeric; pin-pad-char = 0xFF; } 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; } # 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 = 5; max-length = 63; min-length = 4; flags = 0x10; # initialized reference = 1; } PIN so-pin { auth-id = FF; attempts = 5; max-length = 4; min-length = 4; flags = 0xB2; reference = 2 } # Additional filesystem info. # This is added to the file system info specified in the # main profile. filesystem { DF MF { ACL = *=CHV4; path = 3F00; type = DF; # This is the DIR file EF DIR { type = EF; file-id = 2F00; size = 128; acl = *=NONE; } DF PKCS15-AppDF { type = DF; aid = A0:00:00:00:77:01:00:70:0A:10:00:F1:00:00:01:00; file-id = 5015; EF PKCS15-ODF { file-id = 5031; ACL = *=NEVER; ACL = READ=NONE; } EF PKCS15-TokenInfo { file-id = 5032; ACL = *=NEVER; ACL = READ=NONE; } EF PKCS15-AODF { file-id = 7001; ACL = *=NEVER; ACL = READ=NONE; } EF PKCS15-PrKDF { file-id = 7002; ACL = *=NONE; } EF PKCS15-PuKDF { file-id = 7004; ACL = *=NONE; } EF PKCS15-SKDF { file-id = 7003; ACL = *=NONE; } EF PKCS15-CDF { file-id = 7005; ACL = *=NONE; } EF PKCS15-DODF { file-id = 7006; ACL = *=NONE; } BSO template-private-key { ACL = UPDATE=CHV1, DELETE=CHV1; ACL = PSO-DECRYPT=CHV1, INTERNAL-AUTHENTICATE=CHV1, GENERATE=CHV1, PSO-COMPUTE-SIGNATURE=NEVER; } BSO template-public-key { ACL = *=NONE; } EF template-certificate { file-id = B000; ACL = READ=NONE, DELETE=NONE, UPDATE=CHV1, RESIZE=CHV1; } } } } OpenSC-0.26.1/src/pkcs15init/cardos.profile000066400000000000000000000054211474147347300203650ustar00rootroot00000000000000# # PKCS15 r/w profile for Siemens CardOS M4 # smart cards and crypto tokens (for example Aladdin eToken) # 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 = 4096; # Prevent unauthorized updates of basic security # objects via PUT DATA OCI. # ACL = UPDATE=NEVER; ACL = UPDATE=$SOPIN; # Bump the size of the EF(PrKDF) - with split # keys, we may need a little more room. EF PKCS15-PrKDF { size = 1024; } EF PKCS15-PuKDF { size = 768; } EF PKCS15-CDF { size = 1536; } # 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 { } 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 = 3403; 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; } } } } OpenSC-0.26.1/src/pkcs15init/cyberflex.profile000066400000000000000000000055471474147347300211060ustar00rootroot00000000000000# # General purpose PKCS15 profile for Cyberflex Access 16K cards # cardinfo { max-pin-length = 8; pin-encoding = ascii-numeric; pin-pad-char = 0x00; pin-domains = yes; } # Define reasonable limits for PINs and PUK # The user pin must always be CHV1, otherwise things # won't work (crypto operations are protected by CHV1) 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 { # Define default ACLs and file ids for CHV1/CHV2 EF CHV1 { file-id = 0000; ACL = *=NEVER, UPDATE=CHV1; } EF CHV2 { file-id = 0100; ACL = *=NEVER, UPDATE=CHV2; } DF MF { ACL = *=AUT0; # The DELETE=NONE ACLs will go away once the code # works. It's here to make sure I can erase the card # even if I mess up big time. # # If you have a 16K card and wish to store # two cert/key pairs. # Note if you want the two keys to be protected by the # same pin, you need to increase the size of the pin-dir. DF PKCS15-AppDF { ACL = *=$SOPIN, FILES=NONE, DELETE=NONE; # Cyberflex Access 16K size = 7500; # This "pin-domain" DF is a template that is # instantiated for each PIN created on the card. # # 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. That is, PIN 01 will reside # in 4b01, PIN 02 will reside in 4b02, etc. template pin-domain { DF pin-dir { ACL = *=$SOPIN, FILES=NONE, DELETE=NONE; file-id = 4B00; # The minimum size for a 2048 bit key is 1396 size = 2800; } } # For PIN-protected files, instantiate this template # below the pin directory. # For unprotected objects, install within the application DF. # # 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 { # In order to support more than one key per PIN, # each key must be within its own subdirectory. DF key-directory { ACL = *=$PIN, FILES=NONE; file-id = 3000; size = 1400; EF private-key { file-id = 0012; ACL = *=NEVER, CRYPTO=$PIN, UPDATE=$PIN; } EF internal-pubkey-file { file-id = 1012; ACL = *=$PIN, READ=NONE; } } EF extractable-key { file-id = 4300; ACL = *=NEVER, READ=$PIN, UPDATE=$PIN; } EF public-key { file-id = 4400; ACL = *=$PIN, READ=NONE; } EF certificate { file-id = 4500; ACL = *=$PIN, READ=NONE; } EF data { file-id = 4600; ACL = *=$PIN, READ=NONE; } EF privdata { file-id = 4700; ACL = *=$PIN; } } } } } OpenSC-0.26.1/src/pkcs15init/entersafe.profile000066400000000000000000000074601474147347300210730ustar00rootroot00000000000000# # pkcs15 profile for entersafe # cardinfo { manufacturer = "EnterSafe"; min-pin-length = 4; max-pin-length = 16; pin-encoding = ascii-numeric; pin-pad-char = 0x00; } option default { macros { pin-flags = local, initialized, needs-padding; min-pin-length = 4; df_acl = *=NEVER; protected = *=$PIN,READ=NONE; dir-size = 128; tinfo-size = 128; unusedspace-size = 128; odf-size = 256; aodf-size = 256; cdf-size = 512; prkdf-size = 256; pukdf-size = 256; dodf-size = 256; info-size = 128; } } option onepin { macros { pin-flags = local, initialized, needs-padding; df_acl = *=$PIN; protected = *=$PIN,READ=NONE; dir-size = 128; tinfo-size = 128; unusedspace-size = 128; odf-size = 512; aodf-size = 256; cdf-size = 2048; prkdf-size = 1024; pukdf-size = 1024; dodf-size = 256; info-size = 128; } } PIN so-pin { reference = 1; attempts = 3; flags = $pin-flags; min-length = $min-pin-length; } PIN so-puk { reference = 1; attempts = 3; flags = $pin-flags; min-length = $min-pin-length; } PIN user-pin { reference = 1; attempts = 3; flags = $pin-flags; min-length = $min-pin-length; } PIN user-puk { reference = 1; attempts = 3; flags = $pin-flags; min-length = $min-pin-length; } # Additional filesystem info. # This is added to the file system info specified in the # main profile. filesystem { DF MF { ACL = $df_acl; size = 768; EF dir { type = EF; size = $dir-size; ACL = $protected; file-id = 2F00; structure = transparent; } DF PKCS15-AppDF { ACL = $df_acl; size = 16000; # INTERNAL SECRET KEY file of the application DF # Note: if the WRITE ACL is commented out or no # sopin is specified the ACs must be activated via # 'pkcs15-init --finalize' (in this case the # AC WRITE is NEVER as the required state can't # be reached). EF p15_gpkf { file-id = FFFD; structure = transparent; size = 2560; ACL = $df_acl; } EF PKCS15-ODF { size = $odf-size; ACL = $protected; } EF PKCS15-TokenInfo { size = $tinfo-size; ACL = $protected; } EF PKCS15-UnusedSpace { size = $unusedspace-size; ACL = $protected; } EF PKCS15-AODF { size = $aodf-size; ACL = $protected; } EF PKCS15-PrKDF { size = $prkdf-size; ACL = $protected; } EF PKCS15-PuKDF { size = $pukdf-size; ACL = $protected; } EF PKCS15-CDF { size = $cdf-size; ACL = $protected; } EF PKCS15-DODF { size = $dodf-size; ACL = $protected; } template key-domain { BSO private-key { # here ACLs should be defined } EF public-key { file-id = 3000; structure = transparent; ACL = *=NEVER,READ=NONE,UPDATE=$PIN; } # Certificate template EF certificate { file-id = 3100; structure = transparent; ACL = *=NEVER,READ=NONE,UPDATE=$PIN; } # Extractable private keys are stored in transparent EFs. # Encryption of the content is performed by libopensc. EF extractable-key { file-id = 3200; structure = transparent; ACL = *=NEVER,READ=NONE,UPDATE=$PIN; } # data objects are stored in transparent EFs. EF data { file-id = 3300; structure = transparent; ACL = *=NEVER,READ=NONE,UPDATE=NONE; } # data objects are stored in transparent EFs. EF privdata { file-id = 3400; structure = transparent; ACL = *=NEVER,READ=$PIN,UPDATE=$PIN; } } } } } OpenSC-0.26.1/src/pkcs15init/epass2003.profile000066400000000000000000000102641474147347300205330ustar00rootroot00000000000000# # pkcs15 profile for entersafe # cardinfo { manufacturer = "EnterSafe"; min-pin-length = 4; max-pin-length = 16; pin-encoding = ascii-numeric; pin-pad-char = 0x00; } option default { macros { pin-flags = local, initialized, needs-padding; min-pin-length = 4; df_acl = *=NEVER; ef_acl = *=NEVER, READ=NONE, UPDATE=NONE, WRITE=NONE, DELETE=NONE; sf_acl = *=NEVER, UPDATE=NONE; protected = *=$PIN,READ=NONE; unprotected = *=NONE; dir-size = 112; tinfo-size = 128; unusedspace-size = 128; odf-size = 256; aodf-size = 256; cdf-size = 512; prkdf-size = 256; pukdf-size = 256; dodf-size = 256; info-size = 128; maxPin-size = 2; } } option onepin { macros { pin-flags = local, initialized, needs-padding; # df_acl = *=$PIN; df_acl = *=NEVER, CRYPTO=NONE, FILES=NONE, CREATE=NONE, DELETE=NONE; ef_acl = *=NEVER, READ=NONE, UPDATE=NONE, WRITE=NONE, DELETE=NONE; sf_acl = *=NEVER, UPDATE=NONE; protected = *=NEVER,READ=NONE, UPDATE=$PIN, DELETE=$PIN; unprotected = *=NONE; dir-size = 112; tinfo-size = 128; unusedspace-size = 128; odf-size = 512; aodf-size = 256; cdf-size = 2048; prkdf-size = 1024; pukdf-size = 1024; dodf-size = 256; info-size = 128; maxPin-size = 2; } } PIN so-pin { reference = 1; attempts = 6; flags = $pin-flags; min-length = $min-pin-length; } PIN so-puk { attempts = 6; flags = $pin-flags; min-length = $min-pin-length; } PIN user-pin { reference = 2; attempts = 6; flags = $pin-flags; min-length = $min-pin-length; } PIN user-puk { attempts = 6; flags = $pin-flags; min-length = $min-pin-length; } # Additional filesystem info. # This is added to the file system info specified in the # main profile. filesystem { DF MF { ACL = $df_acl; size = 768; file-id = 3F00; aid = 65:6e:74:65:72:73:61:66:65:2d:66:69:70:73 BSO SKey-MF { file-id = 5300; ACL = $sf_acl size = 4; } EF DIR { type = EF; size = $dir-size; ACL = $ef_acl; file-id = 2F00; structure = linear-variable; } DF PKCS15-AppDF { ACL = $df_acl; size = 16000; BSO SKey-AppDF { file-id = 5301; ACL = $sf_acl size = 32; } EF MAXPIN { file-id = 9F00; size = $maxPin-size; ACL = $unprotected; } EF PKCS15-ODF { size = $odf-size; ACL = $protected; } EF PKCS15-TokenInfo { size = $tinfo-size; ACL = $protected; } EF PKCS15-UnusedSpace { size = $unusedspace-size; ACL = $protected; } EF PKCS15-AODF { size = $aodf-size; ACL = $protected; } EF PKCS15-PrKDF { size = $prkdf-size; ACL = $protected; } EF PKCS15-PuKDF { size = $pukdf-size; ACL = $protected; } EF PKCS15-CDF { size = $cdf-size; ACL = $protected; } EF PKCS15-DODF { size = $dodf-size; ACL = $protected; } template key-domain { EF private-key { file-id = 2900; #type = internal-ef; structure = 0xA3; #ACL = READ=CHV1,UPDATE=CHV1,CRYPTO=CHV1; ACL = *=NONE; } EF public-key { file-id = 3000; structure = transparent; ACL = *=NONE; } # Certificate template EF certificate { file-id = 3100; structure = transparent; ACL = READ=NONE,UPDATE=NONE; } # Extractable private keys are stored in transparent EFs. # Encryption of the content is performed by libopensc. EF extractable-key { file-id = 3200; structure = transparent; ACL = *=NEVER,READ=NONE,UPDATE=$PIN; } # data objects are stored in transparent EFs. EF data { file-id = 3300; structure = transparent; ACL = *=NEVER,READ=NONE,UPDATE=NONE; } # data objects are stored in transparent EFs. EF privdata { file-id = 3400; structure = transparent; ACL = *=NEVER,READ=$PIN,UPDATE=$PIN; } } } } } OpenSC-0.26.1/src/pkcs15init/flex.profile000066400000000000000000000061331474147347300200510ustar00rootroot00000000000000# # General purpose PKCS15 profile for Cryptoflex cards # cardinfo { max-pin-length = 8; pin-encoding = ascii-numeric; pin-pad-char = 0x00; pin-domains = yes; } # Define reasonable limits for PINs and PUK # The user pin must always be CHV1, otherwise things # won't work (crypto operations are protected by CHV1) 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 { # Define default ACLs and file ids for CHV1/CHV2 EF CHV1 { file-id = 0000; ACL = *=NEVER, UPDATE=CHV1; size = 23; } EF CHV2 { file-id = 0100; ACL = *=NEVER, UPDATE=CHV2; size = 23; } DF MF { ACL = *=AUT1; # The DELETE=NONE ACLs will go away once the code # works. It's here to make sure I can erase the card # even if I mess up big time. # # If you have a 16K card and wish to store # two cert/key pairs. # Note if you want the two keys to be protected by the # same pin, you need to increase the size of the pin-dir. DF PKCS15-AppDF { ACL = *=$SOPIN, FILES=NONE, DELETE=NONE; #size = 7500; size = 12000; # This "pin-domain" DF is a template that is # instantiated for each PIN created on the card. # # 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. That is, PIN 01 will reside # in 4b01, PIN 02 will reside in 4b02, etc. template pin-domain { DF pin-dir { ACL = *=$SOPIN, FILES=NONE, DELETE=NONE; file-id = 4B00; # The minimum size for a 2048 bit key is 1396 #size = 1396; size = 2792; } } # For PIN-protected files, instantiate this template # below the pin directory. # For unprotected objects, install within the application DF. # # 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. # # VT: The ACLs of the public objects (certificate, public key, non-protected data) # are set to 'NONE'. You can change it and protect operations of your choice # by $SOPIN, but not by $PIN. template key-domain { # In order to support more than one key per PIN, # each key must be within its own subdirectory. DF key-directory { ACL = *=$PIN, FILES=NONE; file-id = 3000; size = 1332; EF private-key { file-id = 0012; ACL = *=NEVER, CRYPTO=$PIN, UPDATE=$PIN; } EF internal-pubkey-file { file-id = 1012; ACL = *=$PIN, READ=NONE; } } EF extractable-key { file-id = 4300; ACL = *=NEVER, READ=$PIN, UPDATE=$PIN; } EF public-key { file-id = 4800; ACL = *=NONE; } EF certificate { file-id = 4500; ACL = *=NONE; } EF data { file-id = 4600; ACL = *=NONE; } EF privdata { file-id = 4700; ACL = *=$PIN; } } } } } OpenSC-0.26.1/src/pkcs15init/gids.profile000066400000000000000000000003461474147347300200410ustar00rootroot00000000000000# # PKCS15 r/w profile for GIDS cards # cardinfo { label = "GIDS"; manufacturer = "MYSMARTLOGON"; max-pin-length = 16; min-pin-length = 4; pin-encoding = ascii-numeric; } OpenSC-0.26.1/src/pkcs15init/ias_adele_admin1.profile000066400000000000000000000116611474147347300222540ustar00rootroot00000000000000# # PKCS15 r/w profile for Oberthur cards # cardinfo { label = "IAS"; manufacturer = "IAS Gemalto"; max-pin-length = 4; min-pin-length = 4; pin-encoding = ascii-numeric; pin-pad-char = 0xFF; } 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; } option ecc { macros { odf-size = 96; aodf-size = 300; cdf-size = 3000; prkdf-size = 6700; pukdf-size = 2300; dodf-size = 3000; skdf-size = 3000; } } # 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 = 5; max-length = 4; min-length = 4; flags = 0x10; # initialized reference = 1; } PIN so-pin { auth-id = FF; attempts = 5; max-length = 4; min-length = 4; flags = 0xB2; reference = 2 } # Additional filesystem info. # This is added to the file system info specified in the # main profile. filesystem { DF MF { ACL = *=CHV4; path = 3F00; type = DF; # This is the DIR file EF DIR { type = EF; file-id = 2F00; size = 128; acl = *=NONE; } # Here comes the application DF DF PKCS15-AppDF { type = DF; aid = E8:28:BD:08:0F:D2:50:00:00:04:01:01; acl = *=NONE; size = 5000; EF PKCS15-ODF { file-id = 5031; size = 96; ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; } EF PKCS15-TokenInfo { file-id = 5032; ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; } } DF Adele-AppDF { type = DF; aid = D2:50:00:00:04:41:64:E8:6C:65:01:01; acl = *=NONE; size = 5000; EF PKCS15-AODF { file-id = 7001; size = 300; ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; } EF PKCS15-PrKDF { file-id = 7002; size = 6700; ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; } EF PKCS15-PuKDF { file-id = 7004; size = 2300; ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; } EF PKCS15-SKDF { file-id = 7003; size = 3000; ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; } EF PKCS15-CDF { file-id = 7005; size = 3000; ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; } EF PKCS15-DODF { file-id = 7006; size = 3000; ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; } template key-domain { # Private RSA keys BSO private-key { ACL = *=NEVER; ACL = SIGN=SCBx17, AUTHENTICATE=SCBx17, DECIPHER=SCBx17, GENERATE=SCBx17, UPDATE=SCBx17, READ=NONE; } # Private DES keys BSO private-des { size = 24; # 192 bits # READ acl used instead of DECIPHER/ENCIPHER/CHECKSUM } # Private data EF private-data { file-id = F000; size = 36; ACL = *=NONE; ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=SCBx17; } # Certificate EF certificate { # for the profiles 'ADELE Admin. 1 & 2' # file-id: auth: A001; sign: A002; encr: A003; # file-id = B000; ACL = *=NEVER; ACL = UPDATE=SCBx17, READ=NONE, DELETE=NONE; } #Public Key BSO public-key { ACL = *=NEVER; ACL = AUTHENTICATE=SCBx17, GENERATE=SCBx17, UPDATE=SCBx17, READ=NONE; } # Public DES keys BSO public-des { size = 24; # 192 bits ACL = *=NONE; } # Public data EF public-data { file-id = D000; ACL = *=NONE; ACL = WRITE=SCBx17, UPDATE=SCBx17, DELETE=NONE; } } } } } OpenSC-0.26.1/src/pkcs15init/ias_adele_admin2.profile000066400000000000000000000116261474147347300222560ustar00rootroot00000000000000# # PKCS15 r/w profile for Oberthur cards # cardinfo { label = "IAS"; manufacturer = "IAS Gemalto"; max-pin-length = 4; min-pin-length = 4; pin-encoding = ascii-numeric; pin-pad-char = 0xFF; # Delete or not the public key when incorporating the # corresponding certificate. keep-public-key = yes; # yes/no } 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; } option ecc { macros { odf-size = 96; aodf-size = 300; cdf-size = 3000; prkdf-size = 6700; pukdf-size = 2300; dodf-size = 3000; skdf-size = 3000; } } # 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 = 5; max-length = 4; min-length = 4; flags = 0x10; # initialized reference = 1; } PIN so-pin { auth-id = FF; attempts = 5; max-length = 4; min-length = 4; flags = 0xB2; reference = 2 } # Additional filesystem info. # This is added to the file system info specified in the # main profile. filesystem { DF MF { ACL = *=CHV4; path = 3F00; type = DF; # This is the DIR file EF DIR { type = EF; file-id = 2F00; size = 128; acl = *=NONE; } # Here comes the application DF DF PKCS15-AppDF { type = DF; aid = E8:28:BD:08:0F:D2:50:00:00:04:02:01; acl = *=NONE; size = 5000; EF PKCS15-ODF { file-id = 5031; size = 96; ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; } EF PKCS15-TokenInfo { file-id = 5032; ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; } EF PKCS15-AODF { file-id = 7001; size = 300; ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; } EF PKCS15-PrKDF { file-id = 7002; size = 6700; ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; } EF PKCS15-PuKDF { file-id = 7004; size = 2300; ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; } EF PKCS15-SKDF { file-id = 7003; size = 3000; ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; } EF PKCS15-CDF { file-id = 7005; size = 3000; ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; } EF PKCS15-DODF { file-id = 7006; size = 3000; ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; } template key-domain { # Private RSA keys BSO private-key { ACL = *=NEVER; ACL = SIGN=SCBx17, AUTHENTICATE=SCBx17, DECIPHER=SCBx17, GENERATE=SCBx17, UPDATE=SCBx17, READ=NONE; } # Private DES keys BSO private-des { size = 24; # 192 bits # READ acl used instead of DECIPHER/ENCIPHER/CHECKSUM } # Private data EF private-data { file-id = F000; size = 36; ACL = *=NONE; ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=SCBx17; } # Certificate EF certificate { # for the profiles 'ADELE Admin. 1 & 2' # file-id: auth: A001; sign: A002; encr: A003; file-id = B000; ACL = *=NEVER; ACL = UPDATE=SCBx17, READ=NONE, DELETE=NONE; } #Public Key BSO public-key { ACL = *=NEVER; ACL = AUTHENTICATE=SCBx17, GENERATE=SCBx17, UPDATE=SCBx17, READ=NONE; } # Public DES keys BSO public-des { size = 24; # 192 bits ACL = *=NONE; } # Public data EF public-data { file-id = D000; ACL = *=NONE; ACL = WRITE=SCBx17, UPDATE=SCBx17, DELETE=NONE; } } } } } OpenSC-0.26.1/src/pkcs15init/ias_adele_common.profile000066400000000000000000000115371474147347300223750ustar00rootroot00000000000000# # PKCS15 r/w profile for Oberthur cards # cardinfo { label = "IAS"; manufacturer = "IAS Gemalto"; max-pin-length = 4; min-pin-length = 4; pin-encoding = ascii-numeric; pin-pad-char = 0xFF; } 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; } option ecc { macros { odf-size = 96; aodf-size = 300; cdf-size = 3000; prkdf-size = 6700; pukdf-size = 2300; dodf-size = 3000; skdf-size = 3000; } } # 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 = 5; max-length = 4; min-length = 4; flags = 0x10; # initialized reference = 1; } PIN so-pin { auth-id = FF; attempts = 5; max-length = 4; min-length = 4; flags = 0xB2; reference = 2 } # Additional filesystem info. # This is added to the file system info specified in the # main profile. filesystem { DF MF { ACL = *=CHV4; path = 3F00; type = DF; # This is the DIR file EF DIR { type = EF; file-id = 2F00; size = 128; acl = *=NONE; } # Here comes the application DF DF PKCS15-AppDF { type = DF; exclusive-aid = E8:28:BD:08:0F:D2:50:00:00:04:03:01; acl = *=NONE; size = 5000; EF PKCS15-ODF { file-id = 5031; size = 96; ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; } EF PKCS15-TokenInfo { file-id = 5032; ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; } EF PKCS15-AODF { file-id = 7001; size = 300; ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; } EF PKCS15-PrKDF { file-id = 7002; size = 6700; ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; } EF PKCS15-PuKDF { file-id = 7004; size = 2300; ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; } EF PKCS15-SKDF { file-id = 7003; size = 3000; ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; } EF PKCS15-CDF { file-id = 7005; size = 3000; ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; } EF PKCS15-DODF { file-id = 7006; size = 3000; ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; } template key-domain { # Private RSA keys BSO private-key { ACL = *=NEVER; ACL = UPDATE=SCBx17, READ=NONE; ACL = PSO-COMPUTE-SIGNATURE=SCBx17, INTERNAL-AUTHENTICATE=SCBx17, PSO-DECRYPT=SCBx17, GENERATE=SCBx17; } # Private DES keys BSO private-des { size = 24; # 192 bits # READ acl used instead of DECIPHER/ENCIPHER/CHECKSUM } # Private data EF private-data { file-id = B200; size = 36; ACL = *=NONE; ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=SCBx17, DELETE=SCBx17; } # Certificate EF certificate { # for the profiles 'ADELE Admin. 1 & 2' # file-id: auth: A001; sign: A002; encr: A003; file-id = B000; ACL = *=NEVER; ACL = UPDATE=SCBx17, READ=NONE, DELETE=NONE; } #Public Key BSO public-key { ACL = *=NEVER; ACL = INTERNAL-AUTHENTICATE=SCBx17, GENERATE=SCBx17, UPDATE=SCBx17, READ=NONE; } # Public DES keys BSO public-des { size = 24; # 192 bits ACL = *=NONE; } # Public data EF public-data { file-id = B100; ACL = *=NONE; ACL = WRITE=SCBx17, UPDATE=SCBx17, DELETE=SCBx17; } } } } } OpenSC-0.26.1/src/pkcs15init/iasecc.profile000066400000000000000000000055321474147347300203440ustar00rootroot00000000000000# # PKCS15 r/w profile for Oberthur cards # cardinfo { label = "IAS"; manufacturer = "IAS Gemalto"; max-pin-length = 4; min-pin-length = 4; pin-encoding = ascii-numeric; pin-pad-char = 0xFF; } 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; } option ecc { macros { odf-size = 96; aodf-size = 300; cdf-size = 3000; prkdf-size = 6700; pukdf-size = 2300; dodf-size = 3000; skdf-size = 3000; } } # 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 = 5; max-length = 4; min-length = 4; flags = 0x10; # initialized reference = 1; } PIN so-pin { auth-id = FF; attempts = 5; max-length = 4; min-length = 4; flags = 0xB2; reference = 2 } # 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. # This is added to the file system info specified in the # main profile. filesystem { DF MF { ACL = *=CHV4; path = 3F00; type = DF; # This is the DIR file EF DIR { type = EF; file-id = 2F00; size = 128; acl = *=NONE; } # Here comes the application DF DF CIA-Adele-AppDF { type = DF; exclusive-aid = E8:28:BD:08:0F:D2:50:00:00:04:01:01; profile-extension = "ias_adele_admin1"; } DF AdeleAdmin2-AppDF { type = DF; exclusive-aid = E8:28:BD:08:0F:D2:50:00:00:04:02:01; profile-extension = "ias_adele_admin2"; } DF AdeleCommon-AppDF { type = DF; exclusive-aid = E8:28:BD:08:0F:D2:50:00:00:04:03:01; profile-extension = "ias_adele_common"; } DF ECCeID-AppDF { type = DF; exclusive-aid = E8:28:BD:08:0F:D2:50:45:43:43:2D:65:49:44; profile-extension = "iasecc_admin_eid"; } DF ECCGeneric-AppDF { type = DF; exclusive-aid = E8:28:BD:08:0F:D2:50:47:65:6E:65:72:69:63; profile-extension = "iasecc_generic_pki"; } DF ECCGenericOberthur-AppDF { type = DF; exclusive-aid = E8:28:BD:08:0F:F2:50:4F:54:20:41:57:50; profile-extension = "iasecc_generic_oberthur"; ACL = *=NONE; ACL = CREATE=SCB0x12; } } } OpenSC-0.26.1/src/pkcs15init/iasecc_admin_eid.profile000066400000000000000000000116461474147347300223400ustar00rootroot00000000000000# # PKCS15 r/w profile for Oberthur cards # cardinfo { label = "ECC v1.0.1"; manufacturer = "Gemalto"; max-pin-length = 4; min-pin-length = 4; pin-encoding = ascii-numeric; pin-pad-char = 0xFF; } 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; # Style of pkcs#15-init support of minidriver: 'none', 'gemalto'; minidriver-support-style = none; } option ecc { macros { odf-size = 96; aodf-size = 300; cdf-size = 3000; prkdf-size = 6700; pukdf-size = 2300; dodf-size = 3000; skdf-size = 3000; } } # 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 = 5; max-length = 4; min-length = 4; flags = 0x10; # initialized reference = 1; } PIN so-pin { auth-id = FF; attempts = 5; max-length = 4; min-length = 4; flags = 0xB2; reference = 2 } # 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. # This is added to the file system info specified in the # main profile. filesystem { DF MF { ACL = *=CHV4; path = 3F00; type = DF; # This is the DIR file EF DIR { type = EF; file-id = 2F00; size = 128; acl = *=NONE; } # Here comes the application DF DF PKCS15-AppDF { type = DF; exclusive-aid = E8:28:BD:08:0F:D2:50:45:43:43:2D:65:49:44; acl = *=NONE; size = 5000; EF PKCS15-ODF { file-id = 5031; size = 60; ACL = WRITE=SCBx44, UPDATE=SCBx44, READ=NONE; } EF PKCS15-TokenInfo { file-id = 5032; size = 400; ACL = WRITE=SCBx44, UPDATE=SCBx44, READ=NONE; } EF PKCS15-AODF { file-id = 7001; size = 225; ACL = WRITE=SCBx44, UPDATE=SCBx44, READ=NONE; } EF PKCS15-PrKDF { file-id = 7002; size = 450; ACL = WRITE=SCBx44, UPDATE=SCBx44, READ=NONE; } EF PKCS15-PuKDF { file-id = 7004; size = 450; ACL = WRITE=SCBx44, UPDATE=SCBx44, READ=NONE; } EF PKCS15-SKDF { file-id = 7003; size = 450; ACL = WRITE=SCBx44, UPDATE=SCBx44, READ=NONE; } EF PKCS15-CDF { file-id = 7005; size = 300; ACL = WRITE=SCBx44, UPDATE=SCBx44, READ=NONE; } EF PKCS15-DODF { file-id = 7006; size = 650; ACL = WRITE=SCBx44, UPDATE=SCBx44, READ=NONE; } template key-domain { # Private RSA keys BSO private-key { ACL = *=NEVER; ACL = PSO-COMPUTE-SIGNATURE=SCBx13, INTERNAL-AUTHENTICATE=SCBx13, PSO-DECRYPT=SCBx13, GENERATE=SCBx44, UPDATE=SCBx44, READ=NONE; } # Private DES keys BSO private-des { size = 24; # 192 bits # READ acl used instead of DECIPHER/ENCIPHER/CHECKSUM } # Private data EF private-data { file-id = E000; size = 36; ACL = *=NONE; ACL = WRITE=SCBx44, UPDATE=SCBx44, READ=SCBx13; } # Certificate EF certificate { file-id = B000; ACL = *=NEVER; ACL = UPDATE=SCBx44, READ=NONE, DELETE=NONE; } #Public Key BSO public-key { ACL = *=NEVER; ACL = INTERNAL-AUTHENTICATE=SCBx13, GENERATE=SCBx44, UPDATE=SCBx44, READ=NONE; } # Public DES keys BSO public-des { size = 24; # 192 bits ACL = *=NONE; } # Public data EF public-data { file-id = B104; ACL = *=NONE; ACL = WRITE=SCBx44, UPDATE=SCBx44; } } } } } OpenSC-0.26.1/src/pkcs15init/iasecc_generic_oberthur.profile000066400000000000000000000110721474147347300237460ustar00rootroot00000000000000# # PKCS15 r/w profile for Oberthur cards # cardinfo { label = "IAS/ECC v1.0.1"; manufacturer = "OpenSC/Oberthur"; max-pin-length = 4; min-pin-length = 4; pin-encoding = ascii-numeric; pin-pad-char = 0xFF; } 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; } option ecc { macros { odf-size = 96; aodf-size = 300; cdf-size = 3000; prkdf-size = 6700; pukdf-size = 2300; dodf-size = 3000; skdf-size = 3000; } } # 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 = 5; max-length = 4; min-length = 4; flags = 0x10; # initialized reference = 0xC1; } PIN so-pin { auth-id = FF; attempts = 5; max-length = 4; min-length = 4; flags = 0xB2; reference = 2 } # Additional filesystem info. # This is added to the file system info specified in the # main profile. filesystem { DF MF { ACL = *=CHV4; path = 3F00; type = DF; # This is the DIR file EF DIR { type = EF; file-id = 2F00; size = 128; ACL = *=NONE; } # Here comes the application DF DF PKCS15-AppDF { type = DF; exclusive-aid = E8:28:BD:08:0F:F2:50:4F:54:20:41:57:50; ACL = *=NONE; ACL = CREATE=SCB0x12; size = 5000; EF PKCS15-ODF { file-id = 5031; ACL = *=NEVER; ACL = READ=NONE; } EF PKCS15-TokenInfo { file-id = 5032; ACL = *=NEVER; ACL = READ=NONE; } EF PKCS15-AODF { file-id = 7001; ACL = *=NEVER; ACL = READ=NONE; } EF PKCS15-PrKDF { file-id = 7002; ACL = *=NEVER; ACL = WRITE=SCB0x12, UPDATE=SCB0x12, READ=NONE; } EF PKCS15-PuKDF { file-id = 7004; ACL = *=NEVER; ACL = WRITE=SCB0x12, UPDATE=SCB0x12, READ=NONE; } EF PKCS15-SKDF { file-id = 7003; ACL = *=NEVER; ACL = WRITE=SCB0x12, UPDATE=SCB0x12, READ=NONE; } EF PKCS15-CDF { file-id = 7005; ACL = WRITE=SCB0x12, UPDATE=SCB0x12, READ=NONE; } EF PKCS15-DODF { file-id = 7006; ACL = *=NEVER; ACL = WRITE=SCB0x12, UPDATE=SCB0x12, READ=NONE; } template key-domain { # Private RSA keys BSO private-key { ACL = *=NEVER; ACL = UPDATE=SCB0x12, READ=NONE; ACL = PSO-COMPUTE-SIGNATURE=SCB0x12, INTERNAL-AUTHENTICATE=SCB0x12, PSO-DECRYPT=SCB0x12, GENERATE=SCB0x12; } # Private DES keys BSO private-des { size = 24; # 192 bits # READ acl used instead of DECIPHER/ENCIPHER/CHECKSUM } # Private data EF private-data { file-id = E000; ACL = *=NEVER; ACL = WRITE=SCB0x12, UPDATE=SCB0x12, READ=SCB0x12; } # Certificate EF certificate { file-id = 3401; ACL = *=NEVER; ACL = UPDATE=SCB0x12, READ=NONE, DELETE=NONE; } #Public Key BSO public-key { ACL = *=NEVER; ACL = INTERNAL-AUTHENTICATE=SCB0x12, GENERATE=SCB0x12, UPDATE=SCB0x12, READ=NONE; } # Public DES keys BSO public-des { size = 24; # 192 bits ACL = *=NONE; } # Public data EF public-data { file-id = F000; ACL = *=NONE; } } } } } OpenSC-0.26.1/src/pkcs15init/iasecc_generic_pki.profile000066400000000000000000000116271474147347300227050ustar00rootroot00000000000000# # PKCS15 r/w profile # cardinfo { label = "IAS/ECC Generic PKI application"; manufacturer = "IAS/ECC OpenSC"; max-pin-length = 4; min-pin-length = 4; pin-encoding = ascii-numeric; pin-pad-char = 0xFF; } 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; # Style of pkcs#15-init support of minidriver: 'none', 'gemalto'; minidriver-support-style = gemalto; } option ecc { macros { odf-size = 96; aodf-size = 300; cdf-size = 3000; prkdf-size = 6700; pukdf-size = 2300; dodf-size = 3000; skdf-size = 3000; } } # 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 = 5; max-length = 4; min-length = 4; flags = 0x10; # initialized reference = 0xC1; } PIN so-pin { auth-id = FF; attempts = 5; max-length = 4; min-length = 4; flags = 0xB2; reference = 2 } # 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. # This is added to the file system info specified in the # main profile. filesystem { DF MF { ACL = *=CHV4; path = 3F00; type = DF; # This is the DIR file EF DIR { type = EF; file-id = 2F00; size = 128; acl = *=NONE; } # Here comes the application DF DF PKCS15-AppDF { type = DF; exclusive-aid = E8:28:BD:08:0F:D2:50:47:65:6E:65:72:69:63; acl = *=NONE; size = 5000; EF PKCS15-ODF { file-id = 5031; size = 96; ACL = WRITE=SCBx13, UPDATE=SCBx13, READ=NONE; } EF PKCS15-TokenInfo { file-id = 5032; ACL = WRITE=SCBx13, UPDATE=SCBx13, READ=NONE; } EF PKCS15-AODF { file-id = 7001; size = 300; ACL = WRITE=SCBx13, UPDATE=SCBx13, READ=NONE; } EF PKCS15-PrKDF { file-id = 7002; size = 6700; ACL = WRITE=SCBx13, UPDATE=SCBx13, READ=NONE; } EF PKCS15-PuKDF { file-id = 7004; size = 2300; ACL = WRITE=SCBx13, UPDATE=SCBx13, READ=NONE; } EF PKCS15-SKDF { file-id = 7003; size = 3000; ACL = WRITE=SCBx13, UPDATE=SCBx13, READ=NONE; } EF PKCS15-CDF { file-id = 7005; size = 3000; ACL = WRITE=SCBx13, UPDATE=SCBx13, READ=NONE; } EF PKCS15-DODF { file-id = 7006; size = 3000; ACL = WRITE=SCBx13, UPDATE=SCBx13, READ=NONE; } template key-domain { # Private RSA keys BSO private-key { ACL = *=NEVER; ACL = UPDATE=SCBx13, READ=NONE; ACL = PSO-DECRYPT=SCBx13, INTERNAL-AUTHENTICATE=SCBx13, GENERATE=SCBx13; } # Private DES keys BSO private-des { size = 24; # 192 bits # READ acl used instead of DECIPHER/ENCIPHER/CHECKSUM } # Private data EF private-data { file-id = E000; size = 36; ACL = *=NONE; ACL = WRITE=SCBx13, UPDATE=SCBx13, READ=SCBx13; } # Certificate EF certificate { file-id = B000; ACL = *=NEVER; ACL = UPDATE=SCBx13, READ=NONE, DELETE=NONE; } #Public Key BSO public-key { ACL = *=NEVER; ACL = INTERNAL-AUTHENTICATE=SCBx13, GENERATE=SCBx13, UPDATE=SCBx13, READ=NONE; } # Public DES keys BSO public-des { size = 24; # 192 bits ACL = *=NONE; } # Public data EF public-data { file-id = B101; ACL = *=NONE; ACL = WRITE=SCBx13, UPDATE=SCBx13, DELETE=NONE; } } } } } OpenSC-0.26.1/src/pkcs15init/isoApplet.profile000066400000000000000000000056731474147347300210630ustar00rootroot00000000000000# # PKCS15 profile for the isoApplet JavaCard Applet. # - init driver: pkcs15-isoApplet.c # - card driver: card-isoApplet.c # cardinfo { label ="JavaCard isoApplet"; manufacturer = "unknown"; min-pin-length = 4; max-pin-length = 16; pin-pad-char = 0x00; } pkcs15 { # Method to calculate ID of the crypto objects # 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' pkcs15-id-style = native; } option default { macros { unusedspace-size = 128; odf-size = 256; aodf-size = 256; cdf-size = 512; prkdf-size = 512; pukdf-size = 512; dodf-size = 256; } } PIN so-pin { attempts = 3; max-length = 16; min-length = 4; reference = 1; flags = case-sensitive, needs-padding, initialized; } PIN so-puk { attempts = 3; max-length = 16; min-length = 16; reference = 2; flags = unblockingPin, unblock-disabled, case-sensitive, change-disabled, initialized; } filesystem { DF MF { path = 3F00; type = DF; # This is the DIR file EF DIR { type = EF; file-id = 2F00; size = 128; acl = *=NONE; } # 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 = *=NONE, DELETE=$PIN; size = 5000; EF PKCS15-ODF { file-id = 5031; size = $odf-size; ACL = *=NONE; } EF PKCS15-TokenInfo { file-id = 5032; ACL = *=NONE; } EF PKCS15-UnusedSpace { file-id = 5033; size = $unusedspace-size; ACL = *=NONE; } EF PKCS15-AODF { file-id = 4401; size = $aodf-size; ACL = *=$PIN, READ=NONE; } EF PKCS15-PrKDF { file-id = 4402; size = $prkdf-size; acl = *=$PIN, READ=NONE; } EF PKCS15-PuKDF { file-id = 4403; size = $pukdf-size; acl = *=$PIN, READ=NONE; } EF PKCS15-CDF { file-id = 4404; size = $cdf-size; acl = *=$PIN, READ=NONE; } EF PKCS15-DODF { file-id = 4405; size = $dodf-size; ACL = *=$PIN, READ=NONE; } template key-domain { BSO private-key { ACL = *=$PIN, READ=NEVER; } # EF extractable-key { # file-id = 3100; # acl = *=NEVER, READ=$PIN, UPDATE=$PIN, # ERASE=$PIN; # } EF data { file-id = 3200; acl = *=NEVER, UPDATE=$PIN, READ=NONE, DELETE-SELF=$PIN, ERASE=$PIN; } EF privdata { file-id = 3500; acl = *=NEVER, UPDATE=$PIN, READ=$PIN, DELETE-SELF=$PIN, ERASE=$PIN; } EF public-key { file-id = 3300; acl = *=NEVER, UPDATE=$PIN, READ=NONE, DELETE-SELF=$PIN, ERASE=$PIN; } EF certificate { file-id = 3400; acl = *=NEVER, UPDATE=$PIN, READ=NONE, DELETE-SELF=$PIN, ERASE=$PIN; } } } } } OpenSC-0.26.1/src/pkcs15init/muscle.profile000066400000000000000000000066051474147347300204070ustar00rootroot00000000000000# # PKCS15 r/w profile for MuscleCards # cardinfo { label = "MUSCLE"; manufacturer = "Identity Alliance"; max-pin-length = 8; min-pin-length = 4; pin-encoding = ascii-numeric; } option default { macros { protected = *=$PIN, READ=NONE; unprotected = *=NONE; so-pin-flags = local, initialized; #, soPin; so-min-pin-length = 4; so-pin-attempts = 2; so-auth-id = 1; so-puk-attempts = 4; so-min-puk-length = 4; unusedspace-size = 128; odf-size = 256; aodf-size = 256; cdf-size = 512; prkdf-size = 256; pukdf-size = 256; dodf-size = 256; } } PIN so-pin { reference = 0; flags = local, initialized; } PIN so-puk { reference = 0; } PIN user-pin { reference = 1; attempts = 3; flags = local, initialized; } PIN user-puk { reference = 1; attempts = 10; } filesystem { DF MF { path = 3F00; type = DF; acl = *=NONE, ERASE=$PIN; # This is the DIR file EF DIR { type = EF; file-id = 2F00; size = 128; acl = *=NONE; } # 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 = *=$PIN; size = 1; # NO DATA SHOULD BE STORED DIRECTLY HERE! EF PKCS15-ODF { file-id = 5031; size = $odf-size; ACL = $unprotected; } EF PKCS15-TokenInfo { file-id = 5032; ACL = $unprotected; size = 128; } 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; } template key-domain { BSO private-key { ACL = *=$PIN, READ=NEVER; } EF public-key { file-id = 3000; structure = transparent; ACL = *=NEVER, READ=NONE, UPDATE=$PIN, ERASE=$PIN; } # Certificate template EF certificate { file-id = 3100; 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 = 3200; structure = transparent; ACL = *=NEVER, READ=$PIN, UPDATE=$PIN, ERASE=$PIN; } # data objects are stored in transparent EFs. EF data { file-id = 3300; structure = transparent; ACL = *=NEVER, READ=NONE, UPDATE=$PIN, ERASE=$PIN; } # private data objects are stored in transparent EFs. EF privdata { file-id = 3400; structure = transparent; ACL = *=NEVER, READ=$PIN, UPDATE=$PIN, ERASE=$PIN; } } } } } OpenSC-0.26.1/src/pkcs15init/myeid.profile000066400000000000000000000152221474147347300202210ustar00rootroot00000000000000# # PKCS15 r/w profile for MyEID cards # cardinfo { label = "MyEID"; manufacturer = "Aventra Ltd."; min-pin-length = 4; max-pin-length = 8; pin-encoding = ascii-numeric; pin-pad-char = 0xFF; } # # 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 = no; } option default { macros { #protected = READ=NONE, UPDATE=CHV1, DELETE=CHV2; #unprotected = READ=NONE, UPDATE=CHV1, DELETE=CHV1; unusedspace-size = 510; odf-size = 255; aodf-size = 255; cdf-size = 1530; cdf-trusted-size = 510; prkdf-size = 1530; pukdf-size = 1530; skdf-size = 1530; dodf-size = 1530; } } # 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 { reference = 1; min-length = 4; max-length = 8; attempts = 3; flags = initialized, needs-padding; } PIN user-puk { min-length = 4; max-length = 8; attempts = 10; flags = needs-padding; } PIN so-pin { reference = 3; auth-id = FF; min-length = 4; max-length = 8; attempts = 3; flags = initialized, soPin, needs-padding; } PIN so-puk { min-length = 4; max-length = 8; attempts = 10; flags = needs-padding; } # Additional filesystem info. # This is added to the file system info specified in the # main profile. filesystem { DF MF { path = 3F00; type = DF; acl = CREATE=$PIN, DELETE=$SOPIN; # This is the DIR file EF DIR { file-id = 2F00; structure = transparent; size = 128; acl = READ=NONE, UPDATE=$SOPIN, DELETE=$SOPIN; } 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 { file-id = 5031; structure = transparent; size = $odf-size; acl = READ=NONE, UPDATE=$PIN, DELETE=$SOPIN; } EF PKCS15-TokenInfo { file-id = 5032; size = 160; structure = transparent; acl = READ=NONE, UPDATE=$SOPIN, DELETE=$SOPIN; } EF PKCS15-UnusedSpace { file-id = 5033; structure = transparent; size = $unusedspace-size; acl = READ=NONE, UPDATE=$SOPIN, DELETE=$SOPIN; } EF PKCS15-AODF { file-id = 4401; structure = transparent; size = $aodf-size; acl = READ=NONE, UPDATE=$SOPIN, DELETE=$SOPIN; } EF PKCS15-PrKDF { file-id = 4402; structure = transparent; size = $prkdf-size; acl = *=NEVER, READ=NONE, UPDATE=$PIN, DELETE=$SOPIN; } EF PKCS15-PuKDF { file-id = 4404; structure = transparent; size = $pukdf-size; acl = *=NEVER, READ=NONE, UPDATE=$PIN, DELETE=$SOPIN; } EF PKCS15-SKDF { file-id = 4407; structure = transparent; size = $skdf-size; acl = *=NEVER, READ=NONE, UPDATE=$PIN, DELETE=$SOPIN; } EF PKCS15-CDF { file-id = 4403; structure = transparent; size = $cdf-size; acl = *=NEVER, READ=NONE, UPDATE=$PIN, DELETE=$SOPIN; } EF PKCS15-CDF-TRUSTED { file-id = 4405; structure = transparent; size = $cdf-trusted-size; acl = *=NEVER, READ=NONE, UPDATE=$PIN, DELETE=$SOPIN; } EF PKCS15-DODF { file-id = 4406; structure = transparent; size = $dodf-size; acl = *=NEVER, READ=NONE, UPDATE=$PIN, DELETE=$SOPIN; } EF template-private-key { type = internal-ef; file-id = 4B01; acl = CRYPTO=$PIN, UPDATE=$PIN, DELETE=$PIN, GENERATE=$PIN; } EF template-secret-key { type = internal-ef; file-id = 4D01; acl = CRYPTO=$PIN, UPDATE=$PIN, DELETE=$PIN, GENERATE=$PIN; } EF template-public-key { structure = transparent; file-id = 5501; acl = READ=NONE, UPDATE=$PIN, DELETE=$PIN, GENERATE=$PIN; } EF template-certificate { file-id = 4301; structure = transparent; acl = READ=NONE, UPDATE=$PIN, DELETE=$PIN; } template key-domain { # This is a dummy entry - pkcs15-init insists that # this is present EF private-key { file-id = 4B01; type = internal-ef; acl = CRYPTO=$PIN, UPDATE=$PIN, DELETE=$PIN, GENERATE=$PIN; } EF public-key { file-id = 5501; structure = transparent; acl = READ=NONE, UPDATE=$PIN, DELETE=$PIN, GENERATE=$PIN; } EF secret-key { file-id = 4D01; type = internal-ef; acl = CRYPTO=$PIN, UPDATE=$PIN, DELETE=$PIN, GENERATE=$PIN; } # Certificate template EF certificate { file-id = 4301; structure = transparent; acl = READ=NONE, UPDATE=$PIN, DELETE=$PIN; } EF privdata { file-id = 4501; structure = transparent; acl = READ=$PIN, UPDATE=$PIN, DELETE=$PIN; } EF data { file-id = 4601; structure = transparent; acl = READ=NONE, UPDATE=$PIN, DELETE=$PIN; } } } } } OpenSC-0.26.1/src/pkcs15init/oberthur.profile000066400000000000000000000105561474147347300207510ustar00rootroot00000000000000# # PKCS15 r/w profile for Oberthur cards # cardinfo { label = "SCM"; manufacturer = "Oberthur/OpenSC"; max-pin-length = 64; min-pin-length = 4; pin-encoding = ascii-numeric; pin-pad-char = 0xFF; } pkcs15 { # Have a lastUpdate field in the EF(TokenInfo)? do-last-update = no; } # 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 = 5; max-length = 64; min-length = 4; flags = case-sensitive, local, initialized, needs-padding; reference = 0x81 } PIN user-puk { attempts = 5; max-length = 64; min-length = 4; flags = case-sensitive, local, unblock-disabled, initialized, needs-padding, unblockingPin; reference = 0x84 } PIN so-pin { auth-id = FF; attempts = 3; max-length = 64; min-length = 4; flags = case-sensitive, unblock-disabled, initialized, needs-padding, soPin; reference = 4 } # 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. # This is added to the file system info specified in the # main profile. filesystem { DF MF { ACL = *=CHV4; DF OberthurAWP-AppDF { ACL = *=NONE; ACL = CREATE=CHV4, CRYPTO=NEVER, PIN-DEFINE=CHV4, PIN-RESET=CHV4; file-id = 5011; size = 40; DF private-DF { ACL = *=NEVER; ACL = CREATE=CHV1, CRYPTO=CHV1, FILES=NONE, DELETE=NONE; file-id = 9002; size = 40; # Private RSA keys EF OberthurAWP-private-key-info { ACL = WRITE=CHV1, UPDATE=CHV1, READ=NONE; } EF template-private-key { file-id = 3000; type = internal-ef; structure = 0xA3; # READ acl used instead of DECRYPT/SIGN ACL = UPDATE=CHV1, READ=CHV1; } # Private DES keys EF OberthurAWP-private-des-info { ACL = WRITE=CHV1, UPDATE=CHV1, READ=NONE; } EF template-private-des { file-id = 4000; type = internal-ef; size = 24; # 192 bits # READ acl used instead of DECRYPT/ENCRYPT/CHECKSUM ACL = UPDATE=CHV1, READ=CHV1; } # Private data EF OberthurAWP-privdata-info { ACL = WRITE=CHV1, UPDATE=CHV1, READ=NONE; } EF template-privdata { file-id = 6000; ACL = WRITE=CHV1, UPDATE=CHV1, READ=CHV1; } } DF public-DF { ACL = CREATE=NONE, CRYPTO=NONE, FILES=NONE, DELETE=NONE; file-id = 9001; size = 80; # Certificate EF OberthurAWP-certificate-info { ACL = WRITE=NONE, UPDATE=NONE, READ=NONE, ERASE=NONE; } EF template-certificate { file-id = 2000; ACL = WRITE=NONE, UPDATE=NONE, READ=NONE, ERASE=NONE; } #Public Key EF OberthurAWP-public-key-info { ACL = WRITE=NONE, UPDATE=NONE, READ=NONE, ERASE=NONE; } EF template-public-key { file-id = 1000; type = internal-ef; structure = 0xA1; ACL = *=NONE; } # Public DES keys EF OberthurAWP-public-des-info { ACL = WRITE=NONE, UPDATE=NONE, READ=NONE, ERASE=NONE; } EF template-public-des { file-id = 7000; type = internal-ef; size = 24; # 192 bits ACL = *=NONE; } # Public data EF OberthurAWP-data-info { ACL = WRITE=NONE, UPDATE=NONE, READ=NONE, ERASE=NONE; } EF template-data { file-id = 5000; ACL = *=NONE; } } EF OberthurAWP-token-info { file-id = 1000; size = 36; ACL = WRITE=CHV4, UPDATE=CHV4, READ=NONE, ERASE=NEVER; } EF OberthurAWP-puk-file { file-id = 2000; size = 16; ACL = WRITE=NEVER, UPDATE=CHV4, READ=NONE, ERASE=NEVER; } EF OberthurAWP-container-list { file-id = 3000; structure = linear-variable; size = 20; record-length = 141; ACL = WRITE=NONE, UPDATE=NONE, READ=NONE, ERASE=NONE; } EF OberthurAWP-public-list { file-id = 4000; size = 250; ACL = *=NONE, ERASE=NEVER; } EF OberthurAWP-private-list { file-id = 5000; size = 125; ACL = WRITE=CHV1, UPDATE=CHV1, READ=NONE, ERASE=NEVER; } } DF PKCS15-AppDF { ACL = *=CHV4, FILES=NONE; size = 20; EF PKCS15-ODF { size = 512; } EF PKCS15-AODF { size = 512; } EF PKCS15-CDF { size = 3072; } EF PKCS15-PrKDF { size = 1024; } EF PKCS15-PuKDF { size = 1024; } EF PKCS15-DODF { size = 512; } } } } OpenSC-0.26.1/src/pkcs15init/openpgp.profile000066400000000000000000000035421474147347300205640ustar00rootroot00000000000000# # PKCS15 profile, generic information. # This profile is loaded before any card specific profile. # cardinfo { min-pin-length = 6; # max length should be overridden in the per-card profile max-pin-length = 12; # To be defined } # Default settings. # This option block will always be processed. option default { macros { protected = *=$SOPIN, READ=NONE; unprotected = *=NONE; so-pin-flags = local, initialized, soPin; so-min-pin-length = 8; so-pin-attempts = 3; so-auth-id = 3; odf-size = 256; aodf-size = 256; cdf-size = 512; prkdf-size = 256; pukdf-size = 256; dodf-size = 256; } } # 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 { attempts = 3; flags = local, initialized; } PIN so-pin { auth-id = $so-auth-id; attempts = $so-pin-attempts; min-length = $so-min-pin-length; flags = $so-pin-flags; } filesystem { DF MF { path = 3F00; type = DF; # This is the DIR file EF DIR { type = EF; file-id = 2F00; acl = *=NONE; } # Here comes the application DF DF PKCS15-AppDF { type = DF; aid = D2:76:00:01:24:01; acl = *=NONE; EF PKCS15-TokenInfo { ACL = $unprotected; } EF PKCS15-PrKDF { size = $prkdf-size; acl = $protected; } EF PKCS15-PuKDF { size = $pukdf-size; acl = $protected; } EF PKCS15-CDF { acl = $unprotected; } # 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 { # This is a dummy entry - pkcs15-init insists that # this is present EF private-key { file-id = 5F48; ACL = *=NEVER, CRYPTO=$PIN, UPDATE=CHV3; } } } } } OpenSC-0.26.1/src/pkcs15init/pkcs15-asepcos.c000066400000000000000000000576471474147347300204560ustar00rootroot00000000000000/* * Copyright (c) 2007 Athena Smartcard Solutions Inc. * * 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 "libopensc/opensc.h" #include "libopensc/cardctl.h" #include "libopensc/log.h" #include "pkcs15-init.h" #include "profile.h" /* delete a EF/DF if present. This function does not return an * error if the requested file is not present. */ static int asepcos_cond_delete(sc_profile_t *pro, sc_pkcs15_card_t *p15card, const sc_path_t *path) { int r; sc_file_t *tfile = NULL; r = sc_select_file(p15card->card, path, &tfile); if (r == SC_SUCCESS) { r = sc_pkcs15init_authenticate(pro, p15card, tfile, SC_AC_OP_DELETE_SELF); sc_file_free(tfile); if (r != SC_SUCCESS) return r; r = sc_delete_file(p15card->card, path); } else if (r == SC_ERROR_FILE_NOT_FOUND) r = SC_SUCCESS; return r; } /* checks whether the file with the transport key exists. If existent * the transport key is verified and stored in the keycache (as a * normal user PIN with the same reference). * @param profile profile information for this card * @param card sc_card_t object to use * @return SC_SUCCESS on success and an error code otherwise */ static int asepcos_check_verify_tpin(sc_profile_t *profile, sc_pkcs15_card_t *p15card) { struct sc_context *ctx = p15card->card->ctx; int r; sc_path_t path; /* check whether the file with the transport PIN exists */ sc_format_path("3f000001", &path); r = sc_select_file(p15card->card, &path, NULL); if (r == SC_SUCCESS) { /* try to verify the transport key */ sc_file_t *tfile = NULL; sc_format_path("3f00", &path); r = sc_profile_get_file_by_path(profile, sc_get_mf_path(), &tfile); if (r != SC_SUCCESS) return r; /* we need to temporarily disable the SC_CARD_CAP_USE_FCI_AC * flag to trick sc_pkcs15init_authenticate() to use access * information form the profile file */ p15card->card->caps &= ~SC_CARD_CAP_USE_FCI_AC; r = sc_pkcs15init_authenticate(profile, p15card, tfile, SC_AC_OP_CRYPTO); p15card->card->caps |= SC_CARD_CAP_USE_FCI_AC; sc_file_free(tfile); LOG_TEST_RET(ctx, r, "unable to authenticate for 'CRYPTO' operation"); } return SC_SUCCESS; } /* erase card: erase all EFs/DFs created by OpenSC * @param profile the sc_profile_t object with the configurable profile * information * @param card the card from which the opensc application should be * erased. * @return SC_SUCCESS on success and an error code otherwise */ static int asepcos_erase(struct sc_profile *profile, sc_pkcs15_card_t *p15card) { int r; sc_path_t path; /* TODO: - only remove the OpenSC entry in EF(DIR) * - use EF(DIR) to get the DF of the OpenSC * pkcs15 application. */ /* Check whether a transport exists and verify it if present */ p15card->opts.use_pin_cache = 1; r = asepcos_check_verify_tpin(profile, p15card); if (r != SC_SUCCESS) return r; /* EF(DIR) */ sc_format_path("3f002f00", &path); r = asepcos_cond_delete(profile, p15card, &path); if (r != SC_SUCCESS) return r; /* DF(PKCS15) */ sc_format_path("3f005015", &path); r = asepcos_cond_delete(profile, p15card, &path); if (r != SC_SUCCESS) return r; return SC_SUCCESS; } /* create application DF * @param profile sc_profile_t object with the configurable profile * information * @param cardd sc_card_t object to be used * @param df sc_file_t with the application DF to create * @return SC_SUCCESS on success and an error value otherwise */ static int asepcos_create_dir(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *df) { int r; static const u8 pa_acl[] = {0x80,0x01,0x5f,0x90,0x00}; sc_file_t *tfile; sc_context_t *ctx = p15card->card->ctx; 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; /* As we don't know whether or not a SO-PIN is used to protect the AC * in the application DF we set the preliminary security attributes * of the DF(PKCS15) to allow everything. Once a SO-PIN is set * we tighten security attributes to values specified in the profile. */ sc_file_dup(&tfile, df); /* we use a separate copy of the sc_file_t object so we don't * override the permissions specified in the profile */ if (tfile == NULL) return SC_ERROR_OUT_OF_MEMORY; r = sc_file_set_sec_attr(tfile, pa_acl, sizeof(pa_acl)); if (r != SC_SUCCESS) { sc_file_free(tfile); return r; } /* create application DF */ r = sc_pkcs15init_create_file(profile, p15card, tfile); sc_file_free(tfile); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r); } /* select PIN reference: do nothing special, the real PIN reference if * determined when the PIN is created. This is just helper function to * determine the next best file id of the PIN file. */ static int asepcos_select_pin_reference(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_auth_info_t *auth_info) { if (auth_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN) return SC_SUCCESS; if (auth_info->attrs.pin.reference <= 0) auth_info->attrs.pin.reference = 1; /* as we want to use + 1 for the PUK we need to * ensure that all references are odd => if the reference is * even add one */ if ((auth_info->attrs.pin.reference & 1) == 0) auth_info->attrs.pin.reference++; return SC_SUCCESS; } /* asepcos_pinid_to_akn: returns the AKN of a PIN EF * This functions calls SELECT FILE and extracts the AKN from the * proprietary FCP attributes. * @param card sc_card_t object to use * @param fileid IN file id of the PIN file * @param akn OUT the AKN of the PIN * @return SC_SUCCESS on success and an error code otherwise */ static int asepcos_pinid_to_akn(sc_card_t *card, int fileid, int *akn) { int r; u8 fid[2]; sc_path_t path; sc_file_t *nfile = NULL; fid[0] = (fileid >> 8) & 0xff; fid[1] = fileid & 0xff; r = sc_path_set(&path, SC_PATH_TYPE_FILE_ID, fid, 2, 0, 0); if (r != SC_SUCCESS) return r; r = sc_select_file(card, &path, &nfile); if (r != SC_SUCCESS) return r; if (nfile->prop_attr == NULL || nfile->prop_attr_len != 11) { sc_log(card->ctx, "unable to determine AKN"); sc_file_free(nfile); return SC_ERROR_INTERNAL; } *akn = nfile->prop_attr[10]; sc_file_free(nfile); return SC_SUCCESS; } static int asepcos_do_store_pin(sc_profile_t *profile, sc_card_t *card, sc_pkcs15_auth_info_t *auth_info, const u8* pin, size_t pinlen, int puk, int pinid) { sc_file_t *nfile = NULL; u8 buf[64], sbuf[64], *p = buf, *q = sbuf; int r, akn = 0; if (auth_info == NULL || auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return SC_ERROR_OBJECT_NOT_VALID; /* outer tag */ *p++ = 0x85; p++; /* as a file id for pin with use 0x00: */ *p++ = (pinid >> 8) & 0xff; *p++ = pinid & 0xff; /* pin length */ if (pinlen < 4 || pinlen > 16) { sc_log(card->ctx, "invalid PIN length"); return SC_ERROR_INVALID_ARGUMENTS; } *p++ = 0x00; *p++ = pinlen & 0xff; /* max tries */ *p++ = auth_info->tries_left & 0xff; /* algorithm id and key key usage and padding bytes */ *p++ = 0x00; *p++ = 0x00; /* key attributes (SO PIN) */ *p++ = 0x00; /* the PIN */ *p++ = 0x81; *p++ = pinlen & 0xff; memcpy(p, pin, pinlen); p += pinlen; /* set outer length */ buf[1] = p - buf - 2; nfile = sc_file_new(); if (nfile == NULL) return SC_ERROR_OUT_OF_MEMORY; nfile->type = SC_FILE_TYPE_INTERNAL_EF; nfile->id = pinid & 0xffff; r = sc_file_set_prop_attr(nfile, buf, p - buf); if (r != SC_SUCCESS) { sc_file_free(nfile); return r; } /* set security attributes */ *q++ = 0x80; *q++ = 0x01; *q++ = 0x92; *q++ = 0xa0; q++; *q++ = 0x89; *q++ = 0x03; *q++ = (pinid >> 16) & 0xff; *q++ = (pinid >> 8 ) & 0xff; *q++ = pinid & 0xff; if (puk != 0) { *q++ = 0x89; *q++ = 0x03; *q++ = (puk >> 16) & 0xff; *q++ = (puk >> 8 ) & 0xff; *q++ = puk & 0xff; } sbuf[4] = q - sbuf - 5; /* we need to set the security attributes separately as PIN itself * is used to protect the UPDATE access permission. */ r = sc_file_set_sec_attr(nfile, sbuf, q - sbuf); if (r != SC_SUCCESS) { sc_file_free(nfile); return r; } r = sc_create_file(card, nfile); sc_file_free(nfile); if (r != SC_SUCCESS) { sc_log(card->ctx, "unable to create PIN file"); return r; } /* get AKN of the newly created PIN */ r = asepcos_pinid_to_akn(card, pinid, &akn); if (r != SC_SUCCESS) return r; /* use the AKN as reference */ auth_info->attrs.pin.reference = akn; /* set the correct PIN length */ auth_info->attrs.pin.min_length = 4; auth_info->attrs.pin.stored_length = pinlen; auth_info->attrs.pin.max_length = 16; return r; } /* simple function to detect whether or not the "onepin" profile is used * (copied from pkcs15-starcos.c). */ static int have_onepin(sc_profile_t *profile) { sc_pkcs15_auth_info_t sopin = {0}; sc_profile_get_pin_info(profile, SC_PKCS15INIT_SO_PIN, &sopin); if (!(sopin.attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN)) return 1; else return 0; } /* create PIN and, if specified, PUK files * @param profile profile information for this card * @param card sc_card_t object to use * @param pin_obj sc_pkcs15_object_t for the PIN * @param pin PIN value * @param len_len PIN length * @param puk PUK value (optional) * @param puk_len PUK length (optional) * @return SC_SUCCESS on success and an error code otherwise */ static int asepcos_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_card *card = p15card->card; int r, pid, puk_id; sc_path_t tpath = df->path; sc_file_t *tfile = NULL; sc_context_t *ctx = p15card->card->ctx; LOG_FUNC_CALLED(ctx); if (!pin || !pin_len) return SC_ERROR_INVALID_ARGUMENTS; 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) | (int)(((tpath.len >> 1) - 1) << 16); /* get the ACL of the application DF */ r = sc_select_file(card, &df->path, &tfile); if (r != SC_SUCCESS) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r); /* verify the PIN protecting the CREATE acl (if necessary) */ r = sc_pkcs15init_authenticate(profile, p15card, tfile, SC_AC_OP_CREATE); sc_file_free(tfile); if (r != SC_SUCCESS) { sc_log(card->ctx, "unable to create PIN file, insufficient rights"); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r); } do { sc_path_t pin_path; memset(&pin_path, 0, sizeof(sc_path_t)); pin_path.type = SC_PATH_TYPE_FILE_ID; /* XXX: check the pkcs15 structure whether this file id * is already used */ r = sc_append_file_id(&pin_path, pid & 0xff); if (r != SC_SUCCESS) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r); r = sc_select_file(card, &pin_path, NULL); if (r == SC_SUCCESS) pid += 2; else if (r != SC_ERROR_FILE_NOT_FOUND) { sc_log(card->ctx, "error selecting PIN file"); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r); } } while (r != SC_ERROR_FILE_NOT_FOUND); if (puk != NULL && puk_len != 0) { /* 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 = {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); else sc_profile_get_pin_info(profile, SC_PKCS15INIT_USER_PUK, &puk_ainfo); /* If a PUK we use "file id of the PIN" + 1 as the file id * of the PUK. */ puk_id = pid + 1; r = asepcos_do_store_pin(profile, card, &puk_ainfo, puk, puk_len, 0, puk_id); if (r != SC_SUCCESS) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r); } else puk_id = 0; r = asepcos_do_store_pin(profile, card, auth_info, pin, pin_len, puk_id, pid); if (r != SC_SUCCESS) SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r); #if 1 if (auth_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN || (have_onepin(profile) && pid == 0x010001)) { sc_cardctl_asepcos_activate_file_t st; /* Once the SO PIN or ,in case of the "onepin" profile", the * first USER PIN has been set we can tighten the ACLs of * the 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); /* remove symbolic references from the ACLs */ r = sc_pkcs15init_fixup_file(profile, p15card, df); if (r != SC_SUCCESS) 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_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) */ /* 1. select MF */ r = sc_select_file(card, sc_get_mf_path(), NULL); if (r != SC_SUCCESS) return r; /* 2. activate the application DF */ st.fileid = df->id; st.is_ef = 0; r = sc_card_ctl(card, SC_CARDCTL_ASEPCOS_ACTIVATE_FILE, &st); if (r != SC_SUCCESS) { sc_log(card->ctx, "unable to activate DF"); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r); } } #endif #ifdef asepcos_USE_PIN_PATH /* using the real path to the PIN file would be nice but unfortunately * it currently causes some problems with the keycache code */ r = sc_append_file_id(&tpath, pid & 0xff); if (r != SC_SUCCESS) return r; auth_info->path = tpath; #endif SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r); } /* internal wrapper for sc_pkcs15init_authenticate() * @param profile information for this card * @param card sc_card_t object to use * @param path path to the EF/DF for which the credential is required * @param op the required access method * @return SC_SUCCESS on success and an error code otherwise */ static int asepcos_do_authenticate(sc_profile_t *profile, sc_pkcs15_card_t *p15card, const sc_path_t *path, int op) { int r; sc_file_t *prkey = NULL; r = sc_profile_get_file_by_path(profile, path, &prkey); if (r != SC_SUCCESS) { 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_log(p15card->card->ctx, "unable to authenticate"); return r; } return SC_SUCCESS; } #define SET_TLV_LENGTH(p,l) do { \ if ((l) < 128) \ *(p)++ = (l) & 0x7f; \ else if ((l) < 256) { \ *(p)++ = 0x81; \ *(p)++ = (l) & 0xff; \ } else { \ *(p)++ = 0x82; \ *(p)++ = ((l) >> 8 ) & 0xff; \ *(p)++ = (l) & 0xff; \ } \ } while(0) static int asepcos_do_create_key(sc_card_t *card, size_t ksize, int fileid, const u8 *keydata, size_t kdlen) { int r; size_t len; sc_file_t *nfile = NULL; u8 buf[1024], *p = buf; if (sizeof(buf) < kdlen + 12) return SC_ERROR_BUFFER_TOO_SMALL; *p++ = 0x85; *p++ = 0x82; p += 2; /* file id */ *p++ = (fileid >> 8) & 0xff; *p++ = fileid & 0xff; /* key size */ *p++ = (ksize >> 8) & 0xff; *p++ = ksize & 0xff; /* max attempts */ *p++ = 0x03; /* key attributes */ *p++ = 0xc0; *p++ = 0x80; *p++ = 0x00; /* key parts */ memcpy(p, keydata, kdlen); p += kdlen; /* set outer TLV length */ len = p - buf - 4; buf[2] = (len >> 8) & 0xff; buf[3] = len & 0xff; nfile = sc_file_new(); if (nfile == NULL) return SC_ERROR_OUT_OF_MEMORY; nfile->type = SC_FILE_TYPE_INTERNAL_EF; nfile->id = fileid & 0xffff; r = sc_file_set_prop_attr(nfile, buf, p - buf); if (r != SC_SUCCESS) { sc_log(card->ctx, "unable to set key prop. attributes"); sc_file_free(nfile); return r; } r = sc_create_file(card, nfile); sc_file_free(nfile); if (r != SC_SUCCESS) { sc_log(card->ctx, "unable to create key file"); return r; } return r; } /* creates a key file */ 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; size_t len; u8 buf[512], *p = buf; size_t blen = kinfo->modulus_length / 8; int afileid = -1, fileid = (kinfo->path.value[kinfo->path.len-2]) << 8 | kinfo->path.value[kinfo->path.len-1]; if (obj->auth_id.len != 0) { /* the key is protected by a PIN */ sc_pkcs15_object_t *pin; struct sc_pkcs15_auth_info *auth_info; sc_cardctl_asepcos_akn2fileid_t st; r = sc_pkcs15_find_pin_by_auth_id(p15card, &obj->auth_id, &pin); if (r != SC_SUCCESS) { sc_log(p15card->card->ctx, "unable to determine reference for the PIN"); return r; } auth_info = (struct sc_pkcs15_auth_info *)pin->data; st.akn = auth_info->attrs.pin.reference; r = sc_card_ctl(p15card->card, SC_CARDCTL_ASEPCOS_AKN2FILEID, &st); if (r != SC_SUCCESS) { sc_log(p15card->card->ctx, "unable to determine file id of the PIN"); return r; } afileid = st.fileid; } /* authenticate if necessary */ r = asepcos_do_authenticate(profile, p15card, &profile->df_info->file->path, SC_AC_OP_CREATE); if (r != SC_SUCCESS) return r; /* first: create private key (file id = 0x0100 | ) */ /* key parts */ *p++ = 0xc1; *p++ = 0x82; p += 2; /* public exponent */ *p++ = 0x90; SET_TLV_LENGTH(p, 3); memset(p, 0xff, 3); p += 3; /* primes p, q */ *p++ = 0x93; SET_TLV_LENGTH(p, blen); memset(p, 0xff, blen); p += blen; /* key TLV length */ len = p - buf - 4; buf[2] = (len >> 8) & 0xff; buf[3] = len & 0xff; /* security attributes */ *p++ = 0x80; *p++ = 0x01; *p++ = 0xa2; /* compute signature and generate key pair */ if (afileid > 0) { *p++ = 0xa0; *p++ = 0x05; *p++ = 0x89; *p++ = 0x03; *p++ = (afileid >> 16) & 0xff; *p++ = (afileid >> 8 ) & 0xff; *p++ = afileid & 0xff; } else { *p++ = 0x90; *p++ = 0x00; } r = asepcos_do_create_key(p15card->card, kinfo->modulus_length, fileid, buf, p - buf); if (r != SC_SUCCESS) { sc_log(p15card->card->ctx, "unable to create private key file"); return r; } kinfo->key_reference = fileid & 0xFF; return r; } /* stores a rsa private key in a internal EF */ static int asepcos_do_store_rsa_key(sc_pkcs15_card_t *p15card, sc_profile_t *profile, sc_pkcs15_object_t *obj, sc_pkcs15_prkey_info_t *kinfo, struct sc_pkcs15_prkey_rsa *key) { int r; size_t klen; u8 buf[512], *p = buf; sc_path_t tpath; sc_cardctl_asepcos_change_key_t ckdata; /* authenticate if necessary */ if (obj->auth_id.len != 0) { r = asepcos_do_authenticate(profile, p15card, &kinfo->path, SC_AC_OP_UPDATE); if (r != SC_SUCCESS) return r; } /* select the rsa private key */ memset(&tpath, 0, sizeof(sc_path_t)); tpath.type = SC_PATH_TYPE_FILE_ID; tpath.len = 2; tpath.value[0] = kinfo->path.value[kinfo->path.len-2]; tpath.value[1] = kinfo->path.value[kinfo->path.len-1]; r = sc_select_file(p15card->card, &tpath, NULL); if (r != SC_SUCCESS) { sc_log(p15card->card->ctx, "unable to select rsa key file"); return r; } /* store key parts in buffer */ *p++ = 0xc1; *p++ = 0x82; p += 2; /* public exponent */ *p++ = 0x90; SET_TLV_LENGTH(p, key->exponent.len); memcpy(p, key->exponent.data, key->exponent.len); p += key->exponent.len; /* primes p, q */ *p++ = 0x93; SET_TLV_LENGTH(p, (key->p.len + key->q.len)); memcpy(p, key->p.data, key->p.len); p += key->p.len; memcpy(p, key->q.data, key->q.len); p += key->q.len; /* key TLV length */ klen = p - buf - 4; buf[2] = (klen >> 8) & 0xff; buf[3] = klen & 0xff; ckdata.data = buf; ckdata.datalen = p - buf; r = sc_card_ctl(p15card->card, SC_CARDCTL_ASEPCOS_CHANGE_KEY, &ckdata); if (r != SC_SUCCESS) { sc_log(p15card->card->ctx, "unable to change key data"); return r; } return SC_SUCCESS; } /* Stores an external (RSA) 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 * @param key the private key * @return SC_SUCCESS on success and an error code otherwise */ static int asepcos_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 *kinfo = (sc_pkcs15_prkey_info_t *) obj->data; if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) { sc_log(p15card->card->ctx, "only RSA is currently supported"); return SC_ERROR_NOT_SUPPORTED; } return asepcos_do_store_rsa_key(p15card, profile, obj, kinfo, &key->u.rsa); } /* Generates a new (RSA) 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 asepcos_generate_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *obj, sc_pkcs15_pubkey_t *pubkey) { int r; sc_pkcs15_prkey_info_t *kinfo = (sc_pkcs15_prkey_info_t *) obj->data; sc_card_t *card = p15card->card; sc_apdu_t apdu; sc_path_t tpath; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE], sbuf[SC_MAX_APDU_BUFFER_SIZE]; /* authenticate if necessary */ r = asepcos_do_authenticate(profile, p15card, &kinfo->path, SC_AC_OP_UPDATE); if (r != SC_SUCCESS) return r; /* select the rsa private key */ memset(&tpath, 0, sizeof(sc_path_t)); tpath.type = SC_PATH_TYPE_FILE_ID; tpath.len = 2; tpath.value[0] = kinfo->path.value[kinfo->path.len-2]; tpath.value[1] = kinfo->path.value[kinfo->path.len-1]; r = sc_select_file(card, &tpath, NULL); if (r != SC_SUCCESS) { sc_log(card->ctx, "unable to select rsa key file"); return r; } sbuf[0] = 0x01; sbuf[1] = 0x00; sbuf[2] = 0x01; sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x46, 0x00, 0x00); apdu.lc = 3; apdu.datalen = 3; apdu.data = sbuf; apdu.le = 256; apdu.resplen = sizeof(rbuf); apdu.resp = rbuf; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) { sc_log(card->ctx, "error creating key"); return SC_ERROR_INTERNAL; } pubkey->u.rsa.modulus.len = apdu.resplen; pubkey->u.rsa.modulus.data = malloc(apdu.resplen); if (pubkey->u.rsa.modulus.data == NULL) return SC_ERROR_OUT_OF_MEMORY; memcpy(pubkey->u.rsa.modulus.data, apdu.resp, apdu.resplen); pubkey->u.rsa.exponent.len = 3; pubkey->u.rsa.exponent.data = malloc(3); if (pubkey->u.rsa.exponent.data == NULL) return SC_ERROR_OUT_OF_MEMORY; memcpy(pubkey->u.rsa.exponent.data, sbuf, 3); kinfo->key_reference = tpath.value[1]; return SC_SUCCESS; } static struct sc_pkcs15init_operations sc_pkcs15init_asepcos_operations = { asepcos_erase, NULL, /* init_card */ asepcos_create_dir, NULL, /* create_domain */ asepcos_select_pin_reference, asepcos_create_pin, NULL, /* select key reference */ asepcos_create_key, asepcos_store_key, asepcos_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_asepcos_ops(void) { return &sc_pkcs15init_asepcos_operations; } OpenSC-0.26.1/src/pkcs15init/pkcs15-authentic.c000066400000000000000000000710731474147347300207720ustar00rootroot00000000000000/* * Specific operations for PKCS #15 initialization of the Oberthur's card * COSMO v7 with applet AuthentIC v3 . * * Copyright (C) 2002 Juha Yrjölä * Copyright (C) 2010 Viktor Tarasov * OpenTrust * * 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 #ifdef ENABLE_OPENSSL /* empty file without openssl */ #include #include #include #include #include #include #include "libopensc/opensc.h" #include "libopensc/cardctl.h" #include "libopensc/log.h" #include "libopensc/pkcs15.h" #include "libopensc/cards.h" #include "libopensc/authentic.h" #include "pkcs15-init.h" #include "profile.h" #define AUTHENTIC_CACHE_TIMESTAMP_PATH "3F0050159999" unsigned char authentic_v3_rsa_mechs[5] = { AUTHENTIC_MECH_CRYPTO_RSA1024, AUTHENTIC_MECH_CRYPTO_RSA1280, AUTHENTIC_MECH_CRYPTO_RSA1536, AUTHENTIC_MECH_CRYPTO_RSA1792, AUTHENTIC_MECH_CRYPTO_RSA2048 }; unsigned char authentic_v3_rsa_ac_ops[6] = { SC_AC_OP_UPDATE, SC_AC_OP_DELETE, SC_AC_OP_PSO_DECRYPT, SC_AC_OP_PSO_COMPUTE_SIGNATURE, SC_AC_OP_INTERNAL_AUTHENTICATE, SC_AC_OP_GENERATE }; struct authentic_ac_access_usage { unsigned ac_op; unsigned access_rule; unsigned usage; }; struct authentic_ac_access_usage authentic_v3_rsa_map_attributes[7] = { {SC_AC_OP_UPDATE, SC_PKCS15_ACCESS_RULE_MODE_UPDATE, 0}, {SC_AC_OP_DELETE, SC_PKCS15_ACCESS_RULE_MODE_DELETE, 0}, {SC_AC_OP_PSO_DECRYPT, SC_PKCS15_ACCESS_RULE_MODE_PSO_DECRYPT, SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP}, {SC_AC_OP_PSO_COMPUTE_SIGNATURE, SC_PKCS15_ACCESS_RULE_MODE_PSO_CDS, SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_NONREPUDIATION}, {SC_AC_OP_INTERNAL_AUTHENTICATE, SC_PKCS15_ACCESS_RULE_MODE_INT_AUTH, SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_SIGNRECOVER}, {SC_AC_OP_GENERATE, SC_PKCS15_ACCESS_RULE_MODE_EXECUTE, 0}, {0, 0, 0} }; int authentic_pkcs15_delete_file(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_file *df); static void authentic_reference_to_pkcs15_id (unsigned int ref, struct sc_pkcs15_id *id) { unsigned ii, sz; for (ii=0, sz = 0; ii> 8*ii) sz++; for (ii=0; ii < sz; ii++) id->value[sz - ii - 1] = (ref >> 8*ii) & 0xFF; id->len = sz; } int authentic_pkcs15_delete_file(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_file *df) { struct sc_context *ctx = p15card->card->ctx; struct sc_card *card = p15card->card; struct sc_path path; unsigned long caps = card->caps; int rv = 0; LOG_FUNC_CALLED(ctx); sc_log(ctx, "delete file(id:%04X)", df->id); card->caps |= SC_CARD_CAP_USE_FCI_AC; rv = sc_pkcs15init_authenticate(profile, p15card, df, SC_AC_OP_DELETE); card->caps = caps; LOG_TEST_RET(ctx, rv, "'DELETE' authentication failed"); memset(&path, 0, sizeof(path)); path.type = SC_PATH_TYPE_FILE_ID; path.value[0] = df->id >> 8; path.value[1] = df->id & 0xFF; path.len = 2; rv = sc_delete_file(card, &path); LOG_FUNC_RETURN(ctx, rv); } /* * Erase the card * */ static int authentic_pkcs15_erase_card(struct sc_profile *profile, struct sc_pkcs15_card *p15card) { struct sc_context *ctx = p15card->card->ctx; struct sc_file *file = NULL; struct sc_pkcs15_df *df; int rv; LOG_FUNC_CALLED(ctx); if (p15card->file_odf) { sc_log(ctx, "Select ODF path: %s", sc_print_path(&p15card->file_odf->path)); rv = sc_select_file(p15card->card, &p15card->file_odf->path, NULL); LOG_TEST_RET(ctx, rv, "Erase application error: cannot select ODF path"); } for (df = p15card->df_list; df; df = df->next) { struct sc_pkcs15_object *objs[32]; unsigned obj_type = 0; int ii; if (df->type == SC_PKCS15_PRKDF) obj_type = SC_PKCS15_TYPE_PRKEY; else if (df->type == SC_PKCS15_PUKDF) obj_type = SC_PKCS15_TYPE_PUBKEY; else if (df->type == SC_PKCS15_CDF) obj_type = SC_PKCS15_TYPE_CERT; else if (df->type == SC_PKCS15_DODF) obj_type = SC_PKCS15_TYPE_DATA_OBJECT; else continue; if (df->enumerated) { 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); if (rv == SC_ERROR_FILE_NOT_FOUND) continue; LOG_TEST_RET(ctx, rv, "Cannot select object data file"); 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); } sc_file_free(file); LOG_TEST_RET(ctx, rv, "Binary erase error"); profile->dirty = 1; } LOG_FUNC_RETURN(ctx, SC_SUCCESS); } /* * Allocate a file */ static int authentic_pkcs15_new_file(struct sc_profile *profile, struct sc_card *card, unsigned int type, unsigned int num, struct sc_file **out) { struct sc_context *ctx = card->ctx; struct sc_file *file = NULL; const char *t_name = NULL; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "type %X; num %i", type, num); switch (type) { case SC_PKCS15_TYPE_PRKEY_RSA: t_name = "template-private-key"; break; case SC_PKCS15_TYPE_PUBKEY_RSA: t_name = "template-public-key"; break; case SC_PKCS15_TYPE_CERT: t_name = "template-certificate"; break; case SC_PKCS15_TYPE_DATA_OBJECT: t_name = "template-public-data"; break; default: LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Profile template not supported"); } sc_log(ctx, "df_info path '%s'", sc_print_path(&profile->df_info->file->path)); rv = sc_profile_get_file(profile, t_name, &file); LOG_TEST_RET(ctx, rv, "Error when getting file from template"); sc_log(ctx, "file(type:%X), path(type:%X,path:%s)", file->type, file->path.type, sc_print_path(&file->path)); file->id = (file->id & 0xFF00) | (num & 0xFF); if (file->type != SC_FILE_TYPE_BSO) { if (file->path.len == 0) { file->path.type = SC_PATH_TYPE_FILE_ID; file->path.len = 2; } file->path.value[file->path.len - 2] = (file->id >> 8) & 0xFF; file->path.value[file->path.len - 1] = file->id & 0xFF; file->path.count = -1; } sc_log(ctx, "file(size:%"SC_FORMAT_LEN_SIZE_T"u,type:%i/%i,id:%04X), path(type:%X,'%s')", file->size, file->type, file->ef_structure, file->id, file->path.type, sc_print_path(&file->path)); if (out) *out = file; else sc_file_free(file); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } /* * Select a key reference */ static int authentic_pkcs15_select_key_reference(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_pkcs15_prkey_info *key_info) { struct sc_context *ctx = p15card->card->ctx; LOG_FUNC_CALLED(ctx); /* In authentic PKCS#15 all crypto objects are locals */ key_info->key_reference |= AUTHENTIC_OBJECT_REF_FLAG_LOCAL; if (key_info->key_reference > AUTHENTIC_V3_CRYPTO_OBJECT_REF_MAX) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); if (key_info->key_reference < AUTHENTIC_V3_CRYPTO_OBJECT_REF_MIN) key_info->key_reference = AUTHENTIC_V3_CRYPTO_OBJECT_REF_MIN; sc_log(ctx, "returns key reference %i", key_info->key_reference); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int authentic_docp_set_acls(struct sc_card *card, struct sc_file *file, unsigned char *ops, size_t ops_len, struct sc_authentic_sdo_docp *docp) { struct sc_context *ctx = card->ctx; unsigned ii, offs; LOG_FUNC_CALLED(ctx); if (ops_len > sizeof(docp->acl_data) / 2) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); for (ii=0, offs=0; iimethod == SC_AC_NEVER) { docp->acl_data[offs++] = 0x00; docp->acl_data[offs++] = 0x00; } else if (entry->method == SC_AC_NONE) { docp->acl_data[offs++] = 0x00; docp->acl_data[offs++] = 0x00; } else if (entry->method == SC_AC_CHV) { if (!(entry->key_ref & AUTHENTIC_V3_CREDENTIAL_ID_MASK) || (entry->key_ref & ~AUTHENTIC_V3_CREDENTIAL_ID_MASK)) LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Non supported Credential Reference"); docp->acl_data[offs++] = 0x00; docp->acl_data[offs++] = 0x01 << (entry->key_ref - 1); } } docp->acl_data_len = offs; LOG_FUNC_RETURN(ctx, offs); } static int authentic_sdo_allocate_prvkey(struct sc_profile *profile, struct sc_card *card, struct sc_pkcs15_prkey_info *key_info, struct sc_authentic_sdo **out) { struct sc_context *ctx = card->ctx; struct sc_authentic_sdo *sdo = NULL; struct sc_file *file = NULL; int rv; LOG_FUNC_CALLED(ctx); if (!out) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); if ((key_info->modulus_length % 256) || key_info->modulus_length < 1024 || key_info->modulus_length > 2048) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); rv = authentic_pkcs15_new_file(profile, card, SC_PKCS15_TYPE_PRKEY_RSA, key_info->key_reference, &file); LOG_TEST_RET(ctx, rv, "Cannot instantiate new PRKEY-RSA file"); sdo = calloc(1, sizeof(struct sc_authentic_sdo)); if (!sdo) { sc_file_free(file); LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot allocate 'sc_authentic_sdo'"); } sdo->magic = AUTHENTIC_SDO_MAGIC; sdo->docp.id = key_info->key_reference & ~AUTHENTIC_OBJECT_REF_FLAG_LOCAL; sdo->docp.mech = authentic_v3_rsa_mechs[(key_info->modulus_length - 1024) / 256]; 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); 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); } static int authentic_pkcs15_add_access_rule(struct sc_pkcs15_object *object, unsigned access_mode, struct sc_pkcs15_id *auth_id) { int ii; for (ii=0;iiaccess_rules[ii].access_mode) { object->access_rules[ii].access_mode = access_mode; if (auth_id) object->access_rules[ii].auth_id = *auth_id; else object->access_rules[ii].auth_id.len = 0; break; } else if (!auth_id && !object->access_rules[ii].auth_id.len) { object->access_rules[ii].access_mode |= access_mode; break; } else if (auth_id && sc_pkcs15_compare_id(&object->access_rules[ii].auth_id, auth_id)) { object->access_rules[ii].access_mode |= access_mode; break; } } if (ii==SC_PKCS15_MAX_ACCESS_RULES) return SC_ERROR_TOO_MANY_OBJECTS; return SC_SUCCESS; } static int authentic_pkcs15_fix_file_access_rule(struct sc_pkcs15_card *p15card, struct sc_file *file, unsigned ac_op, unsigned rule_mode, struct sc_pkcs15_object *object) { struct sc_context *ctx = p15card->card->ctx; const struct sc_acl_entry *acl = NULL; struct sc_pkcs15_id id; unsigned ref; int rv; LOG_FUNC_CALLED(ctx); acl = sc_file_get_acl_entry(file, ac_op); sc_log(ctx, "Fix access rule(op:%i;mode:%i) with ACL(method:%X,ref:%X)", ac_op, rule_mode, acl->method, acl->key_ref); if (acl->method == SC_AC_NEVER) { sc_log(ctx, "ignore access rule(op:%i,mode:%i)", ac_op, rule_mode); } else if (acl->method == SC_AC_NONE) { rv = authentic_pkcs15_add_access_rule(object, rule_mode, NULL); LOG_TEST_RET(ctx, rv, "Fix file access rule error"); } else { sc_log(ctx, "ACL(method:%X,ref:%X)", acl->method, acl->key_ref); if (acl->method == SC_AC_CHV) { ref = acl->key_ref; authentic_reference_to_pkcs15_id (ref, &id); } else { LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Fix file access error"); } sc_log(ctx, "ACL(method:%X,ref:%X)", acl->method, acl->key_ref); rv = authentic_pkcs15_add_access_rule(object, rule_mode, &id); sc_log(ctx, "rv %i", rv); LOG_TEST_RET(ctx, rv, "Fix file access rule error"); } LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int authentic_pkcs15_fix_access(struct sc_pkcs15_card *p15card, struct sc_file *file, struct sc_pkcs15_object *object) { struct sc_context *ctx = p15card->card->ctx; int rv, ii; LOG_FUNC_CALLED(ctx); sc_log(ctx, "authID %s", sc_pkcs15_print_id(&object->auth_id)); memset(object->access_rules, 0, sizeof(object->access_rules)); for (ii=0; authentic_v3_rsa_map_attributes[ii].access_rule; ii++) { rv = authentic_pkcs15_fix_file_access_rule(p15card, file, authentic_v3_rsa_map_attributes[ii].ac_op, authentic_v3_rsa_map_attributes[ii].access_rule, object); LOG_TEST_RET(ctx, rv, "Fix file READ access error"); } LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int authentic_pkcs15_fix_usage(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *object) { struct sc_context *ctx = p15card->card->ctx; int ii, jj; LOG_FUNC_CALLED(ctx); if (object->type == SC_PKCS15_TYPE_PRKEY_RSA) { struct sc_pkcs15_prkey_info *prkey_info = (struct sc_pkcs15_prkey_info *) object->data; sc_log(ctx, "fix private key usage 0x%X", prkey_info->usage); for (ii=0;iiaccess_rules[ii].access_mode) break; for (jj=0; authentic_v3_rsa_map_attributes[jj].access_rule; jj++) if (authentic_v3_rsa_map_attributes[jj].access_rule & object->access_rules[ii].access_mode) prkey_info->usage |= authentic_v3_rsa_map_attributes[jj].usage; } sc_log(ctx, "fixed private key usage 0x%X", prkey_info->usage); } LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static void authentic_free_sdo_data(struct sc_authentic_sdo *sdo) { int rsa_mechs_num = sizeof(authentic_v3_rsa_mechs)/sizeof(authentic_v3_rsa_mechs[0]); int ii; if (!sdo) return; sc_file_free(sdo->file); for (ii=0; iidocp.mech == authentic_v3_rsa_mechs[ii]) break; if (iidata.prvkey); } static int authentic_pkcs15_create_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *object) { struct sc_card *card = p15card->card; struct sc_context *ctx = card->ctx; struct sc_authentic_sdo *sdo = NULL; struct sc_pkcs15_prkey_info *key_info = (struct sc_pkcs15_prkey_info *) object->data; struct sc_file *file_p_prvkey = NULL, *parent = NULL; size_t keybits = key_info->modulus_length; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "create private key(keybits:%"SC_FORMAT_LEN_SIZE_T"u,usage:%X,access:%X,ref:%X)", keybits, key_info->usage, key_info->access_flags, key_info->key_reference); if (keybits < 1024 || keybits > 2048 || (keybits % 256)) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Invalid RSA key size"); rv = authentic_pkcs15_new_file(profile, card, SC_PKCS15_TYPE_PRKEY_RSA, key_info->key_reference, &file_p_prvkey); LOG_TEST_RET(ctx, rv, "IasEcc pkcs15 new PRKEY_RSA file error"); 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); 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); 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) { unsigned long caps = p15card->card->caps; 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_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_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_GOTO_ERR(ctx, rv, "SC_CARDCTL_AUTHENTIC_SDO_CREATE failed"); rv = authentic_pkcs15_fix_access(p15card, file_p_prvkey, object); LOG_TEST_GOTO_ERR(ctx, rv, "cannot fix access rules for private key"); rv = authentic_pkcs15_fix_usage(p15card, object); 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). */ sdo->file = file_p_prvkey; 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_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); } /* * RSA key generation */ static int authentic_pkcs15_generate_key(struct sc_profile *profile, sc_pkcs15_card_t *p15card, struct sc_pkcs15_object *object, struct sc_pkcs15_pubkey *pubkey) { struct sc_card *card = p15card->card; struct sc_context *ctx = card->ctx; struct sc_pkcs15_prkey_info *key_info = (struct sc_pkcs15_prkey_info *) object->data; size_t keybits = key_info->modulus_length; struct sc_authentic_sdo *sdo = NULL; unsigned char *tmp = NULL; size_t tmp_len; unsigned long caps; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "generate key(bits:%"SC_FORMAT_LEN_SIZE_T"u,path:%s,AuthID:%s\n", keybits, sc_print_path(&key_info->path), sc_pkcs15_print_id(&object->auth_id)); if (!object->content.value || object->content.len != sizeof(struct sc_authentic_sdo)) LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Invalid PrKey SDO data"); else if (keybits < 1024 || keybits > 2048 || (keybits % 256)) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Invalid RSA key size"); sdo = (struct sc_authentic_sdo *)object->content.value; if (sdo->magic != AUTHENTIC_SDO_MAGIC) LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "'Magic' control failed for SDO PrvKey"); rv = sc_select_file(card, &key_info->path, NULL); LOG_TEST_RET(ctx, rv, "failed to select parent DF"); caps = card->caps; card->caps &= ~SC_CARD_CAP_USE_FCI_AC; rv = sc_pkcs15init_authenticate(profile, p15card, sdo->file, SC_AC_OP_GENERATE); card->caps = caps; LOG_TEST_RET(ctx, rv, "SC_AC_OP_GENERATE authentication failed"); key_info->access_flags |= SC_PKCS15_PRKEY_ACCESS_LOCAL; rv = sc_card_ctl(card, SC_CARDCTL_AUTHENTIC_SDO_GENERATE, sdo); LOG_TEST_RET(ctx, rv, "generate key failed"); pubkey->algorithm = SC_ALGORITHM_RSA; //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; rv = sc_pkcs15_encode_pubkey(ctx, pubkey, &tmp, &tmp_len); LOG_TEST_RET(ctx, rv, "encode public key failed"); /* * Here algorithms supported by key have to be fixed, if it will be implemented * (see src/libopensc/pkcs15-prkey.c). */ authentic_free_sdo_data(sdo); rv = sc_pkcs15_allocate_object_content(ctx, object, tmp, tmp_len); LOG_TEST_RET(ctx, rv, "Failed to allocate public key as object content"); free(tmp); LOG_FUNC_RETURN(ctx, rv); } /* * Store a private key */ static int authentic_pkcs15_store_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *object, struct sc_pkcs15_prkey *prvkey) { struct sc_card *card = p15card->card; struct sc_context *ctx = card->ctx; struct sc_pkcs15_prkey_info *key_info = (struct sc_pkcs15_prkey_info *) object->data; size_t keybits = key_info->modulus_length; struct sc_authentic_sdo *sdo; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "Store IAS/ECC key(keybits:%"SC_FORMAT_LEN_SIZE_T"u,AuthID:%s,path:%s)", keybits, sc_pkcs15_print_id(&object->auth_id), sc_print_path(&key_info->path)); if (!object->content.value || object->content.len != sizeof(struct sc_authentic_sdo)) LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Invalid PrKey SDO data"); else if (keybits < 1024 || keybits > 2048 || (keybits % 256)) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Invalid RSA key size"); key_info->access_flags &= ~SC_PKCS15_PRKEY_ACCESS_LOCAL; sdo = (struct sc_authentic_sdo *)object->content.value; if (sdo->magic != AUTHENTIC_SDO_MAGIC) LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "'Magic' control failed for SDO PrvKey"); rv = sc_select_file(card, &key_info->path, NULL); LOG_TEST_RET(ctx, rv, "failed to select parent DF"); sdo->data.prvkey = prvkey; 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)); card->caps &= ~SC_CARD_CAP_USE_FCI_AC; rv = sc_pkcs15init_authenticate(profile, p15card, sdo->file, SC_AC_OP_UPDATE); LOG_TEST_RET(ctx, rv, "SC_AC_OP_GENERATE authentication failed"); rv = sc_card_ctl(card, SC_CARDCTL_AUTHENTIC_SDO_STORE, sdo); LOG_TEST_RET(ctx, rv, "store IAS SDO PRIVATE KEY failed"); authentic_free_sdo_data(sdo); sc_pkcs15_free_object_content(object); LOG_FUNC_RETURN(ctx, rv); } static int authentic_pkcs15_delete_rsa_sdo (struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_pkcs15_prkey_info *key_info) { struct sc_context *ctx = p15card->card->ctx; unsigned long caps = p15card->card->caps; struct sc_authentic_sdo sdo; struct sc_file *file = NULL; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "delete SDO RSA key (ref:%i,size:%"SC_FORMAT_LEN_SIZE_T"u)", key_info->key_reference, key_info->modulus_length); rv = authentic_pkcs15_new_file(profile, p15card->card, SC_PKCS15_TYPE_PRKEY_RSA, key_info->key_reference, &file); LOG_TEST_GOTO_ERR(ctx, rv, "PRKEY_RSA instantiation file error"); p15card->card->caps &= ~SC_CARD_CAP_USE_FCI_AC; rv = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_DELETE); p15card->card->caps = caps; LOG_TEST_GOTO_ERR(ctx, rv, "'DELETE' authentication failed for parent RSA key"); sdo.magic = AUTHENTIC_SDO_MAGIC; sdo.docp.id = key_info->key_reference & ~AUTHENTIC_OBJECT_REF_FLAG_LOCAL; sdo.docp.mech = authentic_v3_rsa_mechs[(key_info->modulus_length - 1024) / 256]; rv = sc_card_ctl(p15card->card, SC_CARDCTL_AUTHENTIC_SDO_DELETE, &sdo); if (rv == SC_ERROR_DATA_OBJECT_NOT_FOUND) rv = SC_SUCCESS; LOG_TEST_GOTO_ERR(ctx, rv, "SC_CARDCTL_AUTHENTIC_SDO_DELETE failed for private key"); err: sc_file_free(file); LOG_FUNC_RETURN(ctx, rv); } static int authentic_pkcs15_delete_object (struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *object, const struct sc_path *path) { struct sc_context *ctx = p15card->card->ctx; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "delete PKCS15 object: type %X; path %s\n", object->type, sc_print_path(path)); switch(object->type & SC_PKCS15_TYPE_CLASS_MASK) { case SC_PKCS15_TYPE_PRKEY: rv = authentic_pkcs15_delete_rsa_sdo (profile, p15card, (struct sc_pkcs15_prkey_info *)object->data); LOG_FUNC_RETURN(ctx, rv); case SC_PKCS15_TYPE_PUBKEY: LOG_FUNC_RETURN(ctx, SC_SUCCESS); default: LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); } LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int authentic_store_pubkey(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15_object *object, struct sc_pkcs15_der *data, struct sc_path *path) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_pubkey_info *pubkey_info = (struct sc_pkcs15_pubkey_info *)object->data; struct sc_pkcs15_prkey_info *prkey_info = NULL; struct sc_pkcs15_object *prkey_object = NULL; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "Public Key id '%s'", sc_pkcs15_print_id(&pubkey_info->id)); rv = sc_pkcs15_find_prkey_by_id(p15card, &pubkey_info->id, &prkey_object); LOG_TEST_RET(ctx, rv, "Find related PrKey error"); prkey_info = (struct sc_pkcs15_prkey_info *)prkey_object->data; pubkey_info->key_reference = prkey_info->key_reference; pubkey_info->access_flags = prkey_info->access_flags & SC_PKCS15_PRKEY_ACCESS_LOCAL; pubkey_info->access_flags |= SC_PKCS15_PRKEY_ACCESS_EXTRACTABLE; pubkey_info->native = 0; pubkey_info->usage |= prkey_info->usage & SC_PKCS15_PRKEY_USAGE_SIGN ? SC_PKCS15_PRKEY_USAGE_VERIFY : 0; pubkey_info->usage |= prkey_info->usage & SC_PKCS15_PRKEY_USAGE_SIGNRECOVER ? SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER : 0; pubkey_info->usage |= prkey_info->usage & SC_PKCS15_PRKEY_USAGE_NONREPUDIATION ? SC_PKCS15_PRKEY_USAGE_VERIFY : 0; 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; authentic_pkcs15_add_access_rule(object, SC_PKCS15_ACCESS_RULE_MODE_READ, NULL); /* Here, if key supported algorithms will be implemented (see src/libopensc/pkcs15-prkey.c), * copy private key supported algorithms to the public key's ones. */ LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int authentic_emu_store_data(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15_object *object, struct sc_pkcs15_der *data, struct sc_path *path) { struct sc_context *ctx = p15card->card->ctx; int rv = SC_ERROR_NOT_IMPLEMENTED; LOG_FUNC_CALLED(ctx); switch (object->type & SC_PKCS15_TYPE_CLASS_MASK) { case SC_PKCS15_TYPE_PUBKEY: rv = authentic_store_pubkey(p15card, profile, object, data, path); break; } LOG_FUNC_RETURN(ctx, rv); } static int authentic_emu_update_tokeninfo(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_pkcs15_tokeninfo *tinfo) { struct sc_context *ctx = p15card->card->ctx; struct sc_file *file = NULL; struct sc_path path; unsigned char buffer[8]; int rv; size_t len; 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)); 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); sc_file_free(file); LOG_TEST_RET(ctx, rv, "Update binary error"); } LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int authentic_pkcs15_init_card(struct sc_profile *profile, struct sc_pkcs15_card *p15card) { LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_NOT_SUPPORTED); } static int authentic_pkcs15_create_dir(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_file *df) { LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_NOT_SUPPORTED); } static int authentic_pkcs15_create_pin(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_file *df, struct sc_pkcs15_object *pin_obj, const unsigned char *pin, size_t pin_len, const unsigned char *puk, size_t puk_len) { LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_NOT_SUPPORTED); } static struct sc_pkcs15init_operations sc_pkcs15init_authentic_operations = { authentic_pkcs15_erase_card, authentic_pkcs15_init_card, authentic_pkcs15_create_dir, NULL, /* create_domain */ NULL, /* select_pin_reference */ authentic_pkcs15_create_pin, authentic_pkcs15_select_key_reference, authentic_pkcs15_create_key, authentic_pkcs15_store_key, authentic_pkcs15_generate_key, NULL, /* encode private key */ NULL, /* encode public key */ NULL, /* finalize_card */ authentic_pkcs15_delete_object, /* pkcs15init emulation */ NULL, NULL, authentic_emu_update_tokeninfo, NULL, authentic_emu_store_data, NULL, /* sanity_check */ }; struct sc_pkcs15init_operations * sc_pkcs15init_get_authentic_ops(void) { return &sc_pkcs15init_authentic_operations; } #endif /* ENABLE_OPENSSL */ OpenSC-0.26.1/src/pkcs15init/pkcs15-cardos.c000066400000000000000000000643351474147347300202640ustar00rootroot00000000000000/* * CardOS specific operation for PKCS15 initialization * * Copyright (C) 2005 Nils Larsch * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include #include #include #include #include #include "libopensc/opensc.h" #include "libopensc/cardctl.h" #include "libopensc/log.h" #include "libopensc/cards.h" #include "libopensc/asn1.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; }; /* * Local functions */ static int cardos_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 cardos_create_sec_env(sc_profile_t *, sc_card_t *, unsigned int, unsigned int); static int cardos_put_key(struct sc_profile *, sc_pkcs15_card_t *, int, sc_pkcs15_prkey_info_t *, struct sc_pkcs15_prkey_rsa *); static int cardos_key_algorithm(unsigned int, size_t, int *); static int cardos_extract_pubkey(sc_card_t *, sc_pkcs15_pubkey_t *, sc_file_t *, int); static int do_cardos_extract_pubkey(sc_card_t *card, int nr, u8 tag, sc_pkcs15_bignum_t *bn); static int cardos_have_verifyrc_package(sc_card_t *card); /* 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 CARDOS_PIN_ID_MIN 1 #define CARDOS_PIN_ID_MAX 15 #define CARDOS_KEY_ID_MIN 16 #define CARDOS_KEY_ID_MAX 31 #define CARDOS_AC_NEVER 0xFF #define CARDOS_ALGO_RSA 0x08 #define CARDOS_ALGO_RSA_PURE 0x0C #define CARDOS_ALGO_RSA_SIG 0x88 #define CARDOS_ALGO_RSA_PURE_SIG 0x8C #define CARDOS_ALGO_RSA_SIG_SHA1 0xC8 #define CARDOS_ALGO_RSA_PURE_SIG_SHA1 0xCC #define CARDOS_ALGO_EXT_RSA_PURE 0x0a #define CARDOS_ALGO_EXT_RSA_SIG_PURE 0x8a #define CARDOS_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 int tlv_next(struct tlv *tlv, u8 tag) { if (tlv->next + 2 >= tlv->end) return SC_ERROR_INTERNAL; tlv->current = tlv->next; *(tlv->next++) = tag; *(tlv->next++) = 0; return SC_SUCCESS; } static int tlv_add(struct tlv *tlv, u8 val) { if (tlv->next + 1 >= tlv->end) return SC_ERROR_INTERNAL; *(tlv->next++) = val; tlv->current[1]++; return SC_SUCCESS; } 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 cardos_erase(struct sc_profile *profile, sc_pkcs15_card_t *p15card) { return sc_pkcs15init_erase_card_recursively(p15card, profile); } /* * Create the Application DF */ static int cardos_create_dir(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *df) { int 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 default security environment for this DF. * 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; return 0; } /* * Caller passes in a suggested PIN reference. * See if it's good, and if it isn't, propose something better */ static int cardos_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 = CARDOS_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 > CARDOS_PIN_ID_MAX) return SC_ERROR_TOO_MANY_OBJECTS; auth_info->attrs.pin.reference = preferred; return SC_SUCCESS; } /* * Store a PIN */ static int cardos_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_card *card = p15card->card; unsigned int puk_id = CARDOS_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(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 = {0}; 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 = cardos_store_pin(profile, card, &puk_ainfo, CARDOS_AC_NEVER, puk, puk_len); } if (r >= 0) { r = cardos_store_pin(profile, card, auth_info, puk_id, pin, pin_len); } return r; } /* * Select a key reference */ static int cardos_select_key_reference(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_prkey_info_t *key_info) { if (key_info->key_reference < CARDOS_KEY_ID_MIN) key_info->key_reference = CARDOS_KEY_ID_MIN; if (key_info->key_reference > CARDOS_KEY_ID_MAX) return SC_ERROR_TOO_MANY_OBJECTS; return 0; } /* * Create a private key object. * This is a no-op. */ static int cardos_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 cardos_store_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *obj, sc_pkcs15_prkey_t *key) { struct sc_context *ctx = p15card->card->ctx; sc_pkcs15_prkey_info_t *key_info = (sc_pkcs15_prkey_info_t *) obj->data; struct sc_file *file = NULL; int algorithm = 0, r; if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) { 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_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_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_log(ctx, "Failed to store key: 'UPDATE' authentication failed"); return r; } r = cardos_put_key(profile, p15card, algorithm, key_info, &key->u.rsa); return r; } static void init_key_object(struct sc_pkcs15_prkey_rsa *key, u8 *data, size_t len) { /* Create a key object, initializing components to 0xff */ memset(key, 0x00, sizeof(*key)); memset(data, 0xff, len); key->modulus.data = data; key->modulus.len = len; key->d.data = data; key->d.len = len; key->p.len = len >> 1; key->p.data = data; key->q.len = len >> 1; key->q.data = data; key->iqmp.len = len >> 1; key->iqmp.data = data; key->dmp1.len = len >> 1; key->dmp1.data = data; key->dmq1.len = len >> 1; key->dmq1.data = data; } /* * Key generation */ static int cardos_generate_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *obj, sc_pkcs15_pubkey_t *pubkey) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_prkey_info *key_info = (sc_pkcs15_prkey_info_t *) obj->data; struct sc_pkcs15_prkey_rsa key_obj; struct sc_cardctl_cardos_genkey_info args; struct sc_file *temp; u8 abignum[256]; int algorithm = 0, r, delete_it = 0, use_ext_rsa = 0; size_t keybits, rsa_max_size; int pin_id = -1; if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) return SC_ERROR_NOT_SUPPORTED; 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_log(ctx, "Unable to generate key, max size is %lu", (unsigned long) rsa_max_size); return SC_ERROR_INVALID_ARGUMENTS; } if (keybits > 1024) use_ext_rsa = 1; if (cardos_key_algorithm(key_info->usage, keybits, &algorithm) < 0) { 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_log(ctx, "Profile doesn't define temporary file " "for key generation."); return SC_ERROR_NOT_SUPPORTED; } pin_id = sc_pkcs15init_get_pin_reference(p15card, profile, SC_AC_SYMBOLIC, SC_PKCS15INIT_USER_PIN); if (pin_id >= 0) { r = sc_pkcs15init_verify_secret(profile, p15card, NULL, SC_AC_CHV, pin_id); if (r < 0) return r; } if (use_ext_rsa == 0) temp->ef_structure = SC_FILE_EF_LINEAR_VARIABLE_TLV; else temp->ef_structure = SC_FILE_EF_TRANSPARENT; if ((r = sc_pkcs15init_create_file(profile, p15card, temp)) < 0) goto out; delete_it = 1; init_key_object(&key_obj, abignum, keybits >> 3); r = cardos_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(p15card->card, SC_CARDCTL_CARDOS_GENERATE_KEY, &args); if (r < 0) goto out; r = cardos_extract_pubkey(p15card->card, pubkey, temp, use_ext_rsa); out: if (delete_it != 0) 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; } /* * Object deletion. */ static int cardos_delete_object(sc_profile_t *profile, struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *obj, const struct sc_path *path) { int r = SC_SUCCESS, stored_in_ef = 0, algorithm = 0; size_t keybits; sc_file_t *file = NULL; struct sc_pkcs15_prkey_info *key_info; struct sc_pkcs15_prkey_rsa key_obj; struct sc_context *ctx = p15card->card->ctx; uint8_t abignum[256]; LOG_FUNC_CALLED(ctx); /* * If we are deleting a private key, overwrite it so it can't be used. */ if ((obj->type & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_PRKEY) { key_info = obj->data; keybits = key_info->modulus_length & ~7UL; init_key_object(&key_obj, abignum, keybits >> 3); r = cardos_key_algorithm(key_info->usage, keybits, &algorithm); LOG_TEST_RET(ctx, r, "cardos_key_algorithm failed"); r = sc_select_file(p15card->card, &key_info->path, &file); LOG_TEST_RET(ctx, r, "Failed to store key: cannot select parent DF"); r = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_UPDATE); sc_file_free(file); LOG_TEST_RET(ctx, r, "Failed to store key: UPDATE authentication failed"); r = cardos_put_key(profile, p15card, algorithm, key_info, &key_obj); LOG_TEST_RET(ctx, r, "cardos_put_key failed"); } /* Delete object from the PKCS15 file system. */ if (path->len || path->aid.len) { r = sc_select_file(p15card->card, path, &file); if (r != SC_ERROR_FILE_NOT_FOUND) LOG_TEST_RET(ctx, r, "select object path failed"); stored_in_ef = (file->type != SC_FILE_TYPE_DF); sc_file_free(file); } /* If the object is stored in a normal EF, try to delete the EF. */ if (r == SC_SUCCESS && stored_in_ef) { r = sc_pkcs15init_delete_by_path(profile, p15card, path); LOG_TEST_RET(ctx, r, "Failed to delete object by path"); } LOG_FUNC_RETURN(ctx, SC_SUCCESS); } /* * Store a PIN or PUK */ static int cardos_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_cardos_obj_info args; unsigned char buffer[256]; unsigned char pinpadded[256]; struct tlv tlv; unsigned int attempts, maxlen; u8 minlen; int r, hasverifyrc; 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) { sc_log(card->ctx, "invalid pin length: %"SC_FORMAT_LEN_SIZE_T"u (max %u)\n", pin_len, maxlen); return SC_ERROR_INVALID_ARGUMENTS; } memcpy(pinpadded, pin, pin_len); while (pin_len < maxlen) pinpadded[pin_len++] = profile->pin_pad_char; pin = pinpadded; attempts = auth_info->tries_left; minlen = (u8)auth_info->attrs.pin.min_length; tlv_init(&tlv, buffer, sizeof(buffer)); /* object address: class, id */ 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 */ 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 */ 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. */ if (tlv_add(&tlv, 0xff) != SC_SUCCESS) return SC_ERROR_INTERNAL; /* DEK: not documented, no idea what it means */ 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) */ if (tlv_add(&tlv, 0x00) != SC_SUCCESS || tlv_add(&tlv, minlen) != SC_SUCCESS /* minlen */) return SC_ERROR_INTERNAL; /* AC conditions */ 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 */ if (tlv_next(&tlv, 0x8f) != SC_SUCCESS) return SC_ERROR_INTERNAL; while (pin_len--) if (tlv_add(&tlv, *pin++) != SC_SUCCESS) return SC_ERROR_INTERNAL; args.data = buffer; args.len = tlv_len(&tlv); /* ensure we are in the correct lifecycle */ r = sc_pkcs15init_set_lifecycle(card, SC_CARDCTRL_LIFECYCLE_ADMIN); if (r < 0 && r != SC_ERROR_NOT_SUPPORTED) return r; return sc_card_ctl(card, SC_CARDCTL_CARDOS_PUT_DATA_OCI, &args); } /* * Create an empty security environment */ static int cardos_create_sec_env(struct sc_profile *profile, sc_card_t *card, unsigned int se_id, unsigned int key_id) { struct sc_cardctl_cardos_obj_info args; struct tlv tlv; unsigned char buffer[64]; int r; tlv_init(&tlv, buffer, sizeof(buffer)); 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); /* ensure we are in the correct lifecycle */ r = sc_pkcs15init_set_lifecycle(card, SC_CARDCTRL_LIFECYCLE_ADMIN); if (r < 0 && r != SC_ERROR_NOT_SUPPORTED) return r; return sc_card_ctl(card, SC_CARDCTL_CARDOS_PUT_DATA_SECI, &args); } /* * Determine the key algorithm based on the intended usage * Note that CardOS/M4 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 cardos_key_algorithm(unsigned int usage, size_t keylen, int *algop) { /* if it is sign and decipher, we use decipher and emulate sign */ if (usage & USAGE_ANY_DECIPHER) { if (keylen <= 1024) *algop = CARDOS_ALGO_RSA_PURE; else *algop = CARDOS_ALGO_EXT_RSA_PURE; return 0; } if (usage & USAGE_ANY_SIGN) { if (keylen <= 1024) *algop = CARDOS_ALGO_RSA_PURE_SIG; else *algop = CARDOS_ALGO_EXT_RSA_SIG_PURE; return 0; } return -1; } /* * Create a private key object */ #define CARDOS_KEY_OPTIONS 0x02 #define CARDOS_KEY_FLAGS 0x00 static int cardos_store_key_component(sc_card_t *card, int algorithm, unsigned int key_id, unsigned int pin_id, unsigned int num, const u8 *data, size_t len, int last, int use_prefix) { struct sc_cardctl_cardos_obj_info args; struct tlv tlv; unsigned char buffer[256]; #ifdef SET_SM_BYTES unsigned int n; #endif int r; /* Initialize the TLV encoder */ tlv_init(&tlv, buffer, sizeof(buffer)); /* Object address */ 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 */ 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 */ 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 */ if (tlv_next(&tlv, 0x8B) != SC_SUCCESS) return SC_ERROR_INTERNAL; for (n = 0; n < 16; n++) if (tlv_add(&tlv, 0xFF) != SC_SUCCESS) return SC_ERROR_INTERNAL; #endif /* key component */ if (tlv_next(&tlv, 0x8f) != SC_SUCCESS) return SC_ERROR_INTERNAL; if (use_prefix != 0) { if (tlv_add(&tlv, len+1) != SC_SUCCESS || tlv_add(&tlv, 0) != SC_SUCCESS) return SC_ERROR_INTERNAL; } while (len--) if (tlv_add(&tlv, *data++) != SC_SUCCESS) return SC_ERROR_INTERNAL; args.data = buffer; args.len = tlv_len(&tlv); /* ensure we are in the correct lifecycle */ r = sc_pkcs15init_set_lifecycle(card, SC_CARDCTRL_LIFECYCLE_ADMIN); if (r < 0 && r != SC_ERROR_NOT_SUPPORTED) return r; return sc_card_ctl(card, SC_CARDCTL_CARDOS_PUT_DATA_OCI, &args); } static int cardos_put_key(sc_profile_t *profile, struct sc_pkcs15_card *p15card, int algorithm, sc_pkcs15_prkey_info_t *key_info, struct sc_pkcs15_prkey_rsa *key) { struct sc_card *card = p15card->card; int r, key_id, pin_id; pin_id = sc_pkcs15init_get_pin_reference(p15card, profile, SC_AC_SYMBOLIC, SC_PKCS15INIT_USER_PIN); if (pin_id < 0) pin_id = 0; key_id = key_info->key_reference; if (key_info->modulus_length > 1024 && (card->type == SC_CARD_TYPE_CARDOS_M4_2 || 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)) { r = cardos_store_key_component(card, algorithm, key_id, pin_id, 0, key->p.data, key->p.len, 0, 0); if (r != SC_SUCCESS) return r; r = cardos_store_key_component(card, algorithm, key_id, pin_id, 1, key->q.data, key->q.len, 0, 0); if (r != SC_SUCCESS) return r; r = cardos_store_key_component(card, algorithm, key_id, pin_id, 2, key->dmp1.data, key->dmp1.len, 0, 0); if (r != SC_SUCCESS) return r; r = cardos_store_key_component(card, algorithm, key_id, pin_id, 3, key->dmq1.data, key->dmq1.len, 0, 0); if (r != SC_SUCCESS) return r; r = cardos_store_key_component(card, algorithm, key_id, pin_id, 4, key->iqmp.data, key->iqmp.len, 1, 0); } else { r = cardos_store_key_component(card, algorithm, key_id, pin_id, 0, key->modulus.data, key->modulus.len, 0, 1); if (r != SC_SUCCESS) return r; r = cardos_store_key_component(card, algorithm, key_id, pin_id, 1, key->d.data, key->d.len, 1, 1); } return r; } /* * Extract a key component from the public key file populated by * GENERATE KEY PAIR */ static int parse_ext_pubkey_file(sc_card_t *card, const u8 *data, size_t len, sc_pkcs15_pubkey_t *pubkey) { const u8 *p; size_t ilen = 0, tlen = 0; if (data == NULL || len < 32) return SC_ERROR_INVALID_ARGUMENTS; data = sc_asn1_find_tag(card->ctx, data, len, 0x7f49, &ilen); if (data == NULL) { 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_log(card->ctx, "invalid public key data: missing modulus"); return SC_ERROR_INTERNAL; } pubkey->u.rsa.modulus.len = tlen; pubkey->u.rsa.modulus.data = malloc(tlen); if (pubkey->u.rsa.modulus.data == NULL) return SC_ERROR_OUT_OF_MEMORY; memcpy(pubkey->u.rsa.modulus.data, p, tlen); p = sc_asn1_find_tag(card->ctx, data, ilen, 0x82, &tlen); if (p == NULL) { sc_log(card->ctx, "invalid public key data: missing exponent"); return SC_ERROR_INTERNAL; } pubkey->u.rsa.exponent.len = tlen; pubkey->u.rsa.exponent.data = malloc(tlen); if (pubkey->u.rsa.exponent.data == NULL) return SC_ERROR_OUT_OF_MEMORY; memcpy(pubkey->u.rsa.exponent.data, p, tlen); return SC_SUCCESS; } static int do_cardos_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, 0, 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); if (bn->data == NULL) return SC_ERROR_OUT_OF_MEMORY; memcpy(bn->data, buf + 4, count); return SC_SUCCESS; } static int cardos_extract_pubkey(sc_card_t *card, sc_pkcs15_pubkey_t *pubkey, sc_file_t *tfile, int use_ext_rsa) { int r; memset(pubkey, 0, sizeof(*pubkey)); r = sc_select_file(card, &tfile->path, NULL); if (r != SC_SUCCESS) return r; if (use_ext_rsa == 0) { r = do_cardos_extract_pubkey(card, 1, 0x10, &pubkey->u.rsa.modulus); if (r != SC_SUCCESS) return r; r = do_cardos_extract_pubkey(card, 2, 0x11, &pubkey->u.rsa.exponent); } else { u8 *buf; buf = malloc(tfile->size); if (buf == NULL) return SC_ERROR_OUT_OF_MEMORY; r = sc_read_binary(card, 0, buf, tfile->size, 0); if (r > 0) r = parse_ext_pubkey_file(card, buf, (size_t)r, pubkey); free(buf); } pubkey->algorithm = SC_ALGORITHM_RSA; return r; } 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, *pp; size_t len, tlen = 0, ilen = 0; sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0x01, 0x88); apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.lc = 0; apdu.le = 256; r = sc_transmit_apdu(card, &apdu); 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) { 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, pp, tlen, 0x01, &ilen); if (q == NULL || ilen != 4) return 0; if (q[0] == 0x07) return 1; } 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, pp, tlen, 0x03, &ilen); if (q == NULL || ilen != 4) return 0; if (q[0] == 0x02) return 1; } else { return 0; } p += tlen; len -= tlen + 2; } return 0; } static struct sc_pkcs15init_operations sc_pkcs15init_cardos_operations = { cardos_erase, NULL, /* init_card */ cardos_create_dir, NULL, /* create_domain */ cardos_select_pin_reference, cardos_create_pin, cardos_select_key_reference, cardos_create_key, cardos_store_key, cardos_generate_key, NULL, NULL, /* encode private/public key */ NULL, /* finalize_card */ cardos_delete_object, NULL, NULL, NULL, NULL, NULL, /* pkcs15init emulation */ NULL /* sanity_check */ }; struct sc_pkcs15init_operations * sc_pkcs15init_get_cardos_ops(void) { return &sc_pkcs15init_cardos_operations; } OpenSC-0.26.1/src/pkcs15init/pkcs15-cflex.c000066400000000000000000000615561474147347300201140ustar00rootroot00000000000000/* * Cryptoflex specific operation for PKCS #15 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include #include #include #include "libopensc/opensc.h" #include "libopensc/cardctl.h" #include "libopensc/log.h" #include "pkcs15-init.h" #include "profile.h" static void invert_buf(u8 *dest, const u8 *src, size_t c); static int cflex_create_dummy_chvs(sc_profile_t *, sc_pkcs15_card_t *, sc_file_t *, int, sc_file_t **); static void cflex_delete_dummy_chvs(sc_profile_t *, sc_pkcs15_card_t *, int, sc_file_t **); static int cflex_create_pin_file(sc_profile_t *, sc_pkcs15_card_t *, sc_path_t *, int, const u8 *, size_t, int, const u8 *, size_t, int, sc_file_t **, int); static int cflex_create_empty_pin_file(sc_profile_t *, sc_pkcs15_card_t *, sc_path_t *, int, sc_file_t **); static int cflex_get_keyfiles(sc_profile_t *, sc_card_t *, const sc_path_t *, sc_file_t **, sc_file_t **); unsigned char dummy_pin_value[6] = {0x30, 0x30, 0x30, 0x30, 0x30, 0x30}; static int cflex_delete_file(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *df) { sc_path_t path; sc_file_t *parent; 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) return r; r = sc_pkcs15init_authenticate(profile, p15card, parent, SC_AC_OP_DELETE); sc_file_free(parent); if (r < 0) return r; /* cryptoflex has no ERASE AC */ memset(&path, 0, sizeof(path)); path.type = SC_PATH_TYPE_FILE_ID; path.value[0] = df->id >> 8; path.value[1] = df->id & 0xFF; path.len = 2; r = sc_delete_file(p15card->card, &path); return r; } /* * Erase the card via rm */ static int cflex_erase_card(struct sc_profile *profile, sc_pkcs15_card_t *p15card) { struct sc_context *ctx = p15card->card->ctx; sc_file_t *df = profile->df_info->file, *dir, *userpinfile = NULL; int r; 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 :) * Note we need to delete if before the DF because we create * it *after* the DF. * */ if (sc_profile_get_file(profile, "DIR", &dir) >= 0) { r = cflex_delete_file(profile, p15card, dir); sc_file_free(dir); if (r < 0 && r != SC_ERROR_FILE_NOT_FOUND) goto out; } r=cflex_delete_file(profile, p15card, df); /* If the user pin file isn't in a sub-DF of the pkcs15 DF, delete it */ if (sc_profile_get_file(profile, "pinfile-1", &userpinfile) >= 0 && userpinfile->path.len <= profile->df_info->file->path.len + 2 && memcmp(userpinfile->path.value, profile->df_info->file->path.value, userpinfile->path.len) != 0) { r = cflex_delete_file(profile, p15card, userpinfile); sc_file_free(userpinfile); userpinfile=NULL; } out: /* Forget all cached keys, the pin files on card are all gone. */ sc_file_free(userpinfile); sc_free_apps(p15card->card); if (r == SC_ERROR_FILE_NOT_FOUND) r=0; SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r); } /* * Card initialization. * For the cryptoflex, read the card's serial number from 3F00 0002 */ static int cryptoflex_init_card(sc_profile_t *profile, sc_pkcs15_card_t *p15card) { sc_path_t path; sc_file_t *file; u8 buf[32]; char serial[128]; size_t len; int r; sc_format_path("3F000002", &path); if ((r = sc_select_file(p15card->card, &path, &file)) < 0) { if (r == SC_ERROR_FILE_NOT_FOUND) return 0; return r; } if ((len = file->size) > sizeof(buf)) len = sizeof(buf); sc_file_free(file); if ((r = sc_read_binary(p15card->card, 0, buf, len, 0)) < 0) return r; len = r; if (len == 0) return 0; if ((r = sc_bin_to_hex(buf, len, serial, sizeof(serial), '\0')) < 0) return r; sc_pkcs15init_set_serial(profile, serial); return 0; } /* * Create a DF */ static int cflex_create_dir(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *df) { /* Create the application DF */ return sc_pkcs15init_create_file(profile, p15card, df); } /* * Create a PIN domain (i.e. a sub-directory holding a user PIN) */ static int cflex_create_domain(sc_profile_t *profile, sc_pkcs15_card_t *p15card, const sc_pkcs15_id_t *id, sc_file_t **ret) { return sc_pkcs15_create_pin_domain(profile, p15card, id, ret); } /* * Select the PIN reference */ static int cflex_select_pin_reference(sc_profile_t *profike, sc_pkcs15_card_t *p15card, sc_pkcs15_auth_info_t *auth_info) { int preferred; 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) { preferred = 2; } else { preferred = 1; } if (auth_info->attrs.pin.reference <= preferred) { auth_info->attrs.pin.reference = preferred; return 0; } if (auth_info->attrs.pin.reference > 2) return SC_ERROR_INVALID_ARGUMENTS; /* Caller, please select a different PIN reference */ return SC_ERROR_INVALID_PIN_REFERENCE; } /* * Create a new PIN inside a DF */ static int cflex_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) { struct sc_context *ctx = p15card->card->ctx; 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; sc_file_t *dummies[2]; int ndummies, pin_type, puk_type, r; sc_file_t *file = NULL; LOG_FUNC_CALLED(ctx); if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return SC_ERROR_OBJECT_NOT_VALID; /* If the profile doesn't specify a reference for this PIN, guess */ if (pin_attrs->flags & SC_PKCS15_PIN_FLAG_SO_PIN) { pin_type = SC_PKCS15INIT_SO_PIN; puk_type = SC_PKCS15INIT_SO_PUK; if (pin_attrs->reference != 2) return SC_ERROR_INVALID_ARGUMENTS; } else { pin_type = SC_PKCS15INIT_USER_PIN; puk_type = SC_PKCS15INIT_USER_PUK; if (pin_attrs->reference != 1) return SC_ERROR_INVALID_ARGUMENTS; } /* 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) 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); 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), puk, puk_len, sc_profile_get_pin_retries(profile, puk_type), NULL, 0); cflex_delete_dummy_chvs(profile, p15card, ndummies, dummies); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r); } /* * Create a new key file */ static int cflex_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 *prkf = NULL, *pukf = NULL; size_t size; int r; if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) { sc_log(p15card->card->ctx, "Cryptoflex supports only RSA keys."); return SC_ERROR_NOT_SUPPORTED; } /* Get the public and private key file */ r = cflex_get_keyfiles(profile, p15card->card, &key_info->path, &prkf, &pukf); if (r < 0) return r; /* Adjust the file sizes, if necessary */ switch (key_info->modulus_length) { case 512: size = 166; break; case 768: size = 246; break; case 1024: size = 326; break; case 2048: size = 646; break; default: 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 && prkf->size < size) prkf->size = size; if (pukf && pukf->size < size + 4) pukf->size = size + 4; /* Now create the files */ if ((r = sc_pkcs15init_create_file(profile, p15card, prkf)) < 0 || (r = sc_pkcs15init_create_file(profile, p15card, pukf)) < 0) goto out; key_info->key_reference = 0; out: sc_file_free(prkf); sc_file_free(pukf); return r; } /* * Generate key */ static int cflex_generate_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *obj, sc_pkcs15_pubkey_t *pubkey) { 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; 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_log(card->ctx, "Cryptoflex supports only RSA keys."); return SC_ERROR_NOT_SUPPORTED; } /* Get the public and private key file */ r = cflex_get_keyfiles(profile, card, &key_info->path, &prkf, &pukf); if (r < 0) return r; if (! prkf) return SC_ERROR_NOT_SUPPORTED; /* Make sure we authenticate first */ r = sc_pkcs15init_authenticate(profile, p15card, prkf, SC_AC_OP_CRYPTO); if (r < 0) goto out; keybits = key_info->modulus_length; /* Perform key generation */ memset(&args, 0, sizeof(args)); args.exponent = 0x10001; args.key_bits = keybits; args.key_num = key_info->key_reference; r = sc_card_ctl(card, SC_CARDCTL_CRYPTOFLEX_GENERATE_KEY, &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 = malloc(keybits / 8); pubkey->u.rsa.exponent.len = 3; pubkey->u.rsa.exponent.data = malloc(3); memcpy(pubkey->u.rsa.exponent.data, "\x01\x00\x01", 3); if ((r = sc_select_file(card, &pukf->path, NULL)) < 0 || (r = sc_read_binary(card, 3, raw_pubkey, keybits / 8, 0)) < 0) goto out; invert_buf(pubkey->u.rsa.modulus.data, raw_pubkey, pubkey->u.rsa.modulus.len); out: sc_file_free(pukf); sc_file_free(prkf); return r; } /* * Store a private key */ static int cflex_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; 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_log(card->ctx, "Cryptoflex supports only RSA keys."); return SC_ERROR_NOT_SUPPORTED; } /* Get the public and private key file */ r = cflex_get_keyfiles(profile, card, &key_info->path, &prkf, &pukf); if (r < 0) return r; /* Write the private key */ size = sizeof(keybuf); r = profile->ops->encode_private_key(profile, card, &key->u.rsa, keybuf, &size, key_info->key_reference); if (r < 0) goto out; r = sc_pkcs15init_update_file(profile, p15card, prkf, keybuf, (unsigned)size); if (r < 0) goto out; /* Write the public key */ size = sizeof(keybuf); r = profile->ops->encode_public_key(profile, card, &key->u.rsa, keybuf, &size, key_info->key_reference); if (r < 0) goto out; r = sc_pkcs15init_update_file(profile, p15card, pukf, keybuf, (unsigned)size); out: sc_file_free(prkf); sc_file_free(pukf); return r; } /* * If an access condition references e.g. CHV1, but we don't have * a CHV1 file yet, create an unprotected dummy file in the MF. */ static int cflex_create_dummy_chvs(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *file, int op, sc_file_t **dummies) { struct sc_context *ctx = p15card->card->ctx; const sc_acl_entry_t *acl; int r = 0, ndummies = 0; 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. */ acl = sc_file_get_acl_entry(file, op); for (; acl; acl = acl->next) { sc_path_t parent, ef; if (acl->method != SC_AC_CHV) continue; parent = file->path; parent.len -= 2; r = SC_ERROR_FILE_NOT_FOUND; while (parent.len >= 2 && r == SC_ERROR_FILE_NOT_FOUND) { ef = parent; ef.value[ef.len++] = acl->key_ref - 1; ef.value[ef.len++] = 0; parent.len -= 2; if (ef.len == parent.len && !memcmp(ef.value, parent.value, ef.len)) continue; r = sc_select_file(p15card->card, &ef, NULL); } /* If a valid EF(CHVx) was found, we're fine */ if (r == 0) continue; if (r != SC_ERROR_FILE_NOT_FOUND) break; /* Create a CHV file in the MF */ parent = file->path; parent.len = 2; r = cflex_create_empty_pin_file(profile, p15card, &parent, acl->key_ref, &dummies[ndummies]); if (r < 0) break; ndummies++; } if (r < 0) { cflex_delete_dummy_chvs(profile, p15card, ndummies, dummies); return r; } SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, ndummies); } static void cflex_delete_dummy_chvs(sc_profile_t *profile, sc_pkcs15_card_t *p15card, int ndummies, sc_file_t **dummies) { while (ndummies--) { cflex_delete_file(profile, p15card, dummies[ndummies]); sc_file_free(dummies[ndummies]); } } /* * Create a pin file */ static void put_pin(sc_profile_t *profile, unsigned char *buf, const u8 *pin, size_t len, int retry) { if (len > 8) len = 8; memset(buf, profile->pin_pad_char, 8); memcpy(buf, pin, len); buf[8] = retry; buf[9] = retry; } static int cflex_create_pin_file(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_path_t *df_path, int ref, const u8 *pin, size_t pin_len, int pin_tries, const u8 *puk, size_t puk_len, int puk_tries, sc_file_t **file_ret, int unprotected) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_object *pin_obj = NULL; unsigned char buffer[23]; sc_path_t path; sc_file_t *dummies[2], *file; int r, ndummies; LOG_FUNC_CALLED(ctx); if (file_ret) *file_ret = NULL; /* Build the CHV path */ path = *df_path; path.value[path.len++] = ref - 1; path.value[path.len++] = 0; /* See if the CHV already exists */ r = sc_select_file(p15card->card, &path, NULL); if (r >= 0) return SC_ERROR_FILE_ALREADY_EXISTS; /* Get the file definition from the profile */ 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) LOG_TEST_RET(ctx, SC_ERROR_FILE_NOT_FOUND, "profile does not define pin file ACLs"); file->path = path; file->size = 23; file->id = (ref == 1)? 0x0000 : 0x0100; if (unprotected) { sc_file_add_acl_entry(file, SC_AC_OP_UPDATE, SC_AC_NONE, SC_AC_KEY_REF_NONE); } /* Build the contents of the file */ buffer[0] = buffer[1] = buffer[2] = 0xFF; put_pin(profile, buffer + 3, pin, pin_len, pin_tries); put_pin(profile, buffer + 13, puk, puk_len, puk_tries); /* For updating the file, create a dummy CHV files if * necessary */ ndummies = cflex_create_dummy_chvs(profile, p15card, file, SC_AC_OP_UPDATE, dummies); 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; memset(&pin_cmd, 0, sizeof(pin_cmd)); pin_cmd.cmd = SC_PIN_CMD_VERIFY; pin_cmd.pin_type = SC_AC_CHV; pin_cmd.pin_reference = ref; pin_cmd.pin1.data = dummy_pin_value; pin_cmd.pin1.len = sizeof(dummy_pin_value); r = sc_pin_cmd(p15card->card, &pin_cmd, NULL); if (r < 0) sc_file_free(file); LOG_TEST_RET(ctx, r, "Cannot verify dummy PIN"); }; if (ref == 2) { /* Cache dummy SOPIN value */ r = sc_pkcs15_find_pin_by_type_and_reference(p15card, NULL, SC_AC_CHV, ref, &pin_obj); if (!r && pin_obj) sc_pkcs15_pincache_add(p15card, pin_obj, dummy_pin_value, sizeof(dummy_pin_value)); } r = sc_pkcs15init_create_file(profile, p15card, 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); 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); if (pin_obj) { /* Cache new SOPIN value */ sc_pkcs15_pincache_add(p15card, pin_obj, pin, pin_len); } SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r); } /* * Create a faux pin file */ static int cflex_create_empty_pin_file(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_path_t *path, int ref, sc_file_t **file_ret) { int r; 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, NULL, 0, 0, file_ret, 1); if (r == SC_ERROR_FILE_ALREADY_EXISTS) SC_FUNC_RETURN(p15card->card->ctx, SC_LOG_DEBUG_VERBOSE, r); SC_FUNC_RETURN(p15card->card->ctx, SC_LOG_DEBUG_VERBOSE, r); } /* * Get private and public key file */ static int cflex_get_keyfiles(sc_profile_t *profile, sc_card_t *card, const sc_path_t *df_path, sc_file_t **prkf, sc_file_t **pukf) { sc_path_t path = *df_path; int r; /* Get the private key file */ r = sc_profile_get_file_by_path(profile, &path, prkf); if (r < 0) { char pbuf[SC_MAX_PATH_STRING_SIZE]; r = sc_path_print(pbuf, sizeof(pbuf), &path); if (r != SC_SUCCESS) pbuf[0] = '\0'; sc_log(card->ctx, "Cannot find private key file info " "in profile (path=%s).", pbuf); return r; } /* Get the public key file */ path.len -= 2; sc_append_file_id(&path, 0x1012); r = sc_profile_get_file_by_path(profile, &path, pukf); if (r < 0) { sc_log(card->ctx, "Cannot find public key file info in profile."); sc_file_free(*prkf); return r; } return 0; } static void invert_buf(u8 *dest, const u8 *src, size_t c) { unsigned int i; for (i = 0; i < c; i++) dest[i] = src[c-1-i]; } static int bn2cf(sc_pkcs15_bignum_t *num, u8 *buf, size_t bufsize) { size_t len = num->len; if (len > bufsize) return SC_ERROR_INVALID_ARGUMENTS; invert_buf(buf, num->data, len); while (len < bufsize) buf[len++] = 0; return 0; } static int bn2cft(sc_pkcs15_bignum_t *num, u8 tag, u8 *buf, size_t bufsize) { size_t len = num->len; if (len + 3 > bufsize) return SC_ERROR_INVALID_ARGUMENTS; memset(buf, 0, bufsize); buf[0] = tag; buf[1] = len + 1; memcpy(buf + 3, num->data, len); return 0; } /* * Cryptoflex key encoding */ static int cryptoflex_encode_private_key(sc_profile_t *profile, sc_card_t *card, struct sc_pkcs15_prkey_rsa *rsa, u8 *key, size_t *keysize, int key_ref) { size_t base = rsa->modulus.len / 2, key_blob_size; int r, key_num = key_ref + 1; switch (rsa->modulus.len) { case 512 / 8: case 768 / 8: case 1024 / 8: case 2048 / 8: break; default: return SC_ERROR_INVALID_ARGUMENTS; } key_blob_size = 5 * base + 3; if (*keysize < key_blob_size + 3) return SC_ERROR_BUFFER_TOO_SMALL; *keysize = key_blob_size + 3; *key++ = key_blob_size >> 8; *key++ = key_blob_size & 0xFF; *key++ = key_num; if ((r = bn2cf(&rsa->p, key + 0 * base, base)) < 0 || (r = bn2cf(&rsa->q, key + 1 * base, base)) < 0 || (r = bn2cf(&rsa->iqmp, key + 2 * base, base)) < 0 || (r = bn2cf(&rsa->dmp1, key + 3 * base, base)) < 0 || (r = bn2cf(&rsa->dmq1, key + 4 * base, base)) < 0) return r; key += 5 * base; *key++ = 0; *key++ = 0; *key = 0; return 0; } static int cryptoflex_encode_public_key(sc_profile_t *profile, sc_card_t *card, struct sc_pkcs15_prkey_rsa *rsa, u8 *key, size_t *keysize, int key_ref) { size_t base; int r, key_num = key_ref + 1; switch (rsa->modulus.len) { case 512 / 8: case 768 / 8: case 1024 / 8: case 2048 / 8: break; default: return SC_ERROR_INVALID_ARGUMENTS; } base = rsa->modulus.len / 2; if (*keysize < (5 * base + 10)) return SC_ERROR_BUFFER_TOO_SMALL; *keysize = 5 * base + 10; memset(key, 0, *keysize); *key++ = (5 * base + 7) >> 8; *key++ = (5 * base + 7) & 0xFF; *key++ = key_num; /* Funny code - not sure why we do it this way: * * Specs say: We store: (Length) * modulus modulus (N bytes) * J0 Montgomery const 0 (N/2 bytes) * H Montgomery const 0 (N bytes) * exponent exponent 4 * * --okir */ if ((r = bn2cf(&rsa->modulus, key + 0 * base, 2 * base)) < 0 || (r = bn2cf(&rsa->exponent, key + 5 * base, 4)) < 0) return r; return 0; } /* * Cyberflex key encoding */ static int cyberflex_encode_private_key(sc_profile_t *profile, sc_card_t *card, struct sc_pkcs15_prkey_rsa *rsa, u8 *key, size_t *keysize, int key_ref) { size_t base = rsa->modulus.len / 2, key_blob_size, bnlen; int r, key_num = key_ref + 1, alg_id; switch (rsa->modulus.len) { case 512 / 8: alg_id = 0xC4; break; case 768 / 8: alg_id = 0xC6; break; case 1024 / 8: alg_id = 0xC8; break; default: return SC_ERROR_INVALID_ARGUMENTS; } key_blob_size = 12 + 5 * (base + 3) + 4; if (*keysize < key_blob_size) return SC_ERROR_BUFFER_TOO_SMALL; *keysize = key_blob_size; memset(key, 0, *keysize); *key++ = key_blob_size >> 8; *key++ = key_blob_size & 0xFF; *key++ = key_num; *key++ = alg_id; /* key blob header: * "C2:06:C1:08:13:00:00:05" */ memcpy(key, "\xc2\x06\xc1\x08\x12\x00\x00\x05", 8); key += 8; /* Each bignum is encoded with a 2 byte header and a * NULL pad byte */ bnlen = base + 3; if ((r = bn2cft(&rsa->q, 0xC2, key + 0 * bnlen, bnlen)) < 0 || (r = bn2cft(&rsa->p, 0xC2, key + 1 * bnlen, bnlen)) < 0 || (r = bn2cft(&rsa->iqmp, 0xC2, key + 2 * bnlen, bnlen)) < 0 || (r = bn2cft(&rsa->dmq1, 0xC2, key + 3 * bnlen, bnlen)) < 0 || (r = bn2cft(&rsa->dmp1, 0xC2, key + 4 * bnlen, bnlen)) < 0) return r; key += 5 * bnlen; key[0] = 0x0A; key[1] = 0x0A; key[2] = 0x00; key[3] = 0x00; return 0; } static int cyberflex_encode_public_key(sc_profile_t *profile, sc_card_t *card, struct sc_pkcs15_prkey_rsa *rsa, u8 *key, size_t *keysize, int key_ref) { size_t base = rsa->modulus.len, key_blob_size, bnlen; int r, key_num = key_ref + 1, alg_id; switch (rsa->modulus.len) { case 512 / 8: alg_id = 0xC5; break; case 768 / 8: alg_id = 0xC7; break; case 1024 / 8: alg_id = 0xC9; break; default: return SC_ERROR_INVALID_ARGUMENTS; } key_blob_size = 12 + 3 + base + 7 + 4; if (*keysize < key_blob_size) return SC_ERROR_BUFFER_TOO_SMALL; *keysize = key_blob_size; memset(key, 0, *keysize); *key++ = key_blob_size >> 8; *key++ = key_blob_size & 0xFF; *key++ = key_num; *key++ = alg_id; /* Key blob header */ memcpy(key, "\xC1\x06\xC0\x08\x13\x00\x00\x05", 8); key += 8; bnlen = rsa->modulus.len + 3; if ((r = bn2cft(&rsa->modulus, 0xC0, key, bnlen)) < 0 || (r = bn2cft(&rsa->exponent, 0xC0, key + bnlen, 3 + 4)) < 0) return r; key += bnlen + 3 + 4; key[0] = 0x0A; key[1] = 0x0A; key[2] = 0x00; key[3] = 0x00; return 0; } static struct sc_pkcs15init_operations sc_pkcs15init_cryptoflex_operations = { cflex_erase_card, cryptoflex_init_card, cflex_create_dir, cflex_create_domain, cflex_select_pin_reference, cflex_create_pin, NULL, /* select_key_reference */ cflex_create_key, cflex_store_key, cflex_generate_key, cryptoflex_encode_private_key, cryptoflex_encode_public_key, NULL, /* finalize_card */ NULL, /* delete_object */ NULL, NULL, NULL, NULL, NULL, /* pkcs15init emulation */ NULL /* sanity_check */ }; static struct sc_pkcs15init_operations sc_pkcs15init_cyberflex_operations = { cflex_erase_card, NULL, /* init_card */ cflex_create_dir, cflex_create_domain, cflex_select_pin_reference, cflex_create_pin, NULL, /* select_key_reference */ cflex_create_key, cflex_store_key, cflex_generate_key, cyberflex_encode_private_key, cyberflex_encode_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_cryptoflex_ops(void) { return &sc_pkcs15init_cryptoflex_operations; } struct sc_pkcs15init_operations * sc_pkcs15init_get_cyberflex_ops(void) { return &sc_pkcs15init_cyberflex_operations; } OpenSC-0.26.1/src/pkcs15init/pkcs15-entersafe.c000066400000000000000000000404751474147347300207640ustar00rootroot00000000000000/* * 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 */ /* Initially written by Weitao Sun (weitao@ftsafe.com) 2008*/ /* Disable RSA:512bits by Shengchao Niu (shengchao@ftsafe.com) 2012 */ #include "config.h" #include #include #include #include #include #include "libopensc/log.h" #include "libopensc/opensc.h" #include "libopensc/cardctl.h" #include "pkcs15-init.h" #include "profile.h" static u8 process_acl_entry(sc_file_t *in, unsigned int method, unsigned int in_def) { u8 def = (u8)in_def; const sc_acl_entry_t *entry = sc_file_get_acl_entry(in, method); if (!entry) { return def; } 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_SYMBOLIC) { return ENTERSAFE_AC_ALWAYS&0x04; } else if (entry->method == SC_AC_NEVER) { return ENTERSAFE_AC_NEVER; } else { return def; } } static int entersafe_erase_card(struct sc_profile *profile, sc_pkcs15_card_t *p15card) { SC_FUNC_CALLED(p15card->card->ctx, SC_LOG_DEBUG_VERBOSE); if (sc_select_file(p15card->card, sc_get_mf_path(), NULL) < 0) return SC_SUCCESS; return sc_card_ctl(p15card->card,SC_CARDCTL_ERASE_CARD,0); } static int entersafe_init_card(sc_profile_t *profile, sc_pkcs15_card_t *p15card) { struct sc_card *card = p15card->card; int ret; {/* MF */ sc_file_t *mf_file; sc_entersafe_create_data mf_data; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); ret = sc_profile_get_file(profile, "MF", &mf_file); 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; mf_data.data.df.file_id[1]=0x00; mf_data.data.df.file_count=0x04; mf_data.data.df.flag=0x11; mf_data.data.df.ikf_size[0]=(mf_file->size>>8)&0xFF; mf_data.data.df.ikf_size[1]=mf_file->size&0xFF; mf_data.data.df.create_ac=0x10; mf_data.data.df.append_ac=0xC0; mf_data.data.df.lock_ac=0x10; memcpy(mf_data.data.df.aid,mf_file->name,mf_file->namelen); sc_file_free(mf_file); ret = sc_card_ctl(card, SC_CARDCTL_ENTERSAFE_CREATE_FILE, &mf_data); LOG_TEST_RET(card->ctx,ret,"Create MF failed"); } {/* EF(DIR) */ sc_file_t *dir_file; size_t fid,size; sc_entersafe_create_data ef_data; u8 *buff=0; /* get dir profile */ ret = sc_profile_get_file(profile, "dir", &dir_file); LOG_TEST_RET(card->ctx,ret,"Get EF(DIR) info failed"); fid=dir_file->id; size=dir_file->size; sc_file_free(dir_file); ef_data.type=SC_ENTERSAFE_EF_DATA; ef_data.data.ef.file_id[0]=(fid>>8)&0xFF; ef_data.data.ef.file_id[1]=fid&0xFF; ef_data.data.ef.size[0]=(size>>8)&0xFF; ef_data.data.ef.size[1]=size&0xFF; ef_data.data.ef.attr[0]=0x00; ef_data.data.ef.attr[1]=0x00; ef_data.data.ef.name=0x00; memset(ef_data.data.ef.ac,0x10,sizeof(ef_data.data.ef.ac)); 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); 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); memset(buff,0,size); ret = sc_update_binary(card,0,buff,size,0); free(buff); LOG_TEST_RET(card->ctx,ret,"Initialize EF(DIR) failed"); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,SC_SUCCESS); } static int entersafe_create_dir(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *df) { struct sc_card *card = p15card->card; int ret; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); {/* df */ sc_entersafe_create_data df_data; df_data.type = SC_ENTERSAFE_DF_DATA; df_data.data.df.file_id[0]=(df->id >> 8) & 0xFF; df_data.data.df.file_id[1]=df->id & 0xFF; df_data.data.df.file_count=0x30; df_data.data.df.flag=0x01; df_data.data.df.ikf_size[0]=(df->size>>8)&0xFF; df_data.data.df.ikf_size[1]=df->size&0xFF; df_data.data.df.create_ac=0x10; df_data.data.df.append_ac=0xC0; df_data.data.df.lock_ac=0x10; memcpy(df_data.data.df.aid,df->name,df->namelen); ret = sc_card_ctl(card, SC_CARDCTL_ENTERSAFE_CREATE_FILE, &df_data); LOG_TEST_RET(card->ctx,ret,"Create DF failed"); } {/* GPKF */ sc_file_t *gpkf_file; sc_entersafe_create_data ef_data; /* get p15_gpkf profile */ ret = sc_profile_get_file(profile, "p15_gpkf", &gpkf_file); 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; ef_data.data.ef.file_id[1]=gpkf_file->id&0xFF; ef_data.data.ef.size[0]=(gpkf_file->size>>8)&0xFF; ef_data.data.ef.size[1]=gpkf_file->size&0xFF; ef_data.data.ef.attr[0]=0x15; ef_data.data.ef.attr[1]=0x80; ef_data.data.ef.name=0x00; memset(ef_data.data.ef.ac,0x10,sizeof(ef_data.data.ef.ac)); memset(ef_data.data.ef.sm,0x00,sizeof(ef_data.data.ef.sm)); sc_file_free(gpkf_file); ret = sc_card_ctl(card, SC_CARDCTL_ENTERSAFE_CREATE_FILE, &ef_data); LOG_TEST_RET(card->ctx,ret,"Create GPKF failed"); } {/* p15 efs */ const char * create_efs[]={ "PKCS15-ODF", "PKCS15-TokenInfo", "PKCS15-UnusedSpace", "PKCS15-AODF", "PKCS15-PrKDF", "PKCS15-PuKDF", "PKCS15-CDF", "PKCS15-DODF", NULL, }; int i; sc_file_t *file=0; sc_entersafe_create_data tmp; for(i = 0; create_efs[i]; ++i) { if (sc_profile_get_file(profile, create_efs[i], &file)) { 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); } tmp.type=SC_ENTERSAFE_EF_DATA; tmp.data.ef.file_id[0]=(file->id>>8)&0xFF; tmp.data.ef.file_id[1]=file->id&0xFF; tmp.data.ef.size[0]=(file->size>>8)&0xFF; tmp.data.ef.size[1]=file->size&0xFF; tmp.data.ef.attr[0]=0x00; tmp.data.ef.attr[1]=0x00; tmp.data.ef.name=0x00; memset(tmp.data.ef.ac,ENTERSAFE_AC_ALWAYS,sizeof(tmp.data.ef.ac)); tmp.data.ef.ac[0]=process_acl_entry(file,SC_AC_OP_READ,ENTERSAFE_AC_ALWAYS); /* read */ tmp.data.ef.ac[1]=process_acl_entry(file,SC_AC_OP_UPDATE,ENTERSAFE_AC_ALWAYS); /* update */ memset(tmp.data.ef.sm,0x00,sizeof(tmp.data.ef.sm)); sc_file_free(file); ret = sc_card_ctl(card, SC_CARDCTL_ENTERSAFE_CREATE_FILE, &tmp); LOG_TEST_RET(card->ctx,ret,"Create pkcs15 file failed"); } } {/* Preinstall keys */ ret = sc_card_ctl(card, SC_CARDCTL_ENTERSAFE_PREINSTALL_KEYS, 0); LOG_TEST_RET(card->ctx,ret,"Preinstall keys failed"); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,ret); } static int entersafe_pin_reference(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_auth_info_t *auth_info) { 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 (auth_info->attrs.pin.reference < ENTERSAFE_USER_PIN_ID) auth_info->attrs.pin.reference = ENTERSAFE_USER_PIN_ID; if (auth_info->attrs.pin.reference > ENTERSAFE_USER_PIN_ID) return SC_ERROR_TOO_MANY_OBJECTS; SC_FUNC_RETURN(p15card->card->ctx, SC_LOG_DEBUG_VERBOSE,SC_SUCCESS); } static int entersafe_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) { struct sc_card *card = p15card->card; int r; sc_pkcs15_auth_info_t *auth_info = (sc_pkcs15_auth_info_t *) pin_obj->data; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return SC_ERROR_OBJECT_NOT_VALID; {/*pin*/ sc_entersafe_wkey_data data; if (!pin || !pin_len || pin_len > 16) return SC_ERROR_INVALID_ARGUMENTS; data.key_id = auth_info->attrs.pin.reference; data.usage=0x0B; data.key_data.symmetric.EC=0x33; data.key_data.symmetric.ver=0x00; /* pad pin with 0 */ memset(data.key_data.symmetric.key_val, 0, sizeof(data.key_data.symmetric.key_val)); memcpy(data.key_data.symmetric.key_val, pin, pin_len); 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); } {/*puk*/ sc_entersafe_wkey_data data; if (!puk || !puk_len || puk_len > 16) return SC_ERROR_INVALID_ARGUMENTS; data.key_id = auth_info->attrs.pin.reference+1; data.usage=0x0B; data.key_data.symmetric.EC=0x33; data.key_data.symmetric.ver=0x00; /* pad pin with 0 */ memset(data.key_data.symmetric.key_val, 0, sizeof(data.key_data.symmetric.key_val)); memcpy(data.key_data.symmetric.key_val, puk, puk_len); data.key_data.symmetric.key_len=16; r = sc_card_ctl(card, SC_CARDCTL_ENTERSAFE_WRITE_KEY, &data); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } static int entersafe_key_reference(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_prkey_info_t *prkey) { SC_FUNC_CALLED(p15card->card->ctx, SC_LOG_DEBUG_VERBOSE); if (prkey->key_reference < ENTERSAFE_MIN_KEY_ID) prkey->key_reference = ENTERSAFE_MIN_KEY_ID; if (prkey->key_reference > ENTERSAFE_MAX_KEY_ID) return SC_ERROR_TOO_MANY_OBJECTS; SC_FUNC_RETURN(p15card->card->ctx, SC_LOG_DEBUG_VERBOSE,SC_SUCCESS); } static int entersafe_create_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *obj) { SC_FUNC_CALLED(p15card->card->ctx, SC_LOG_DEBUG_VERBOSE); SC_FUNC_RETURN(p15card->card->ctx, SC_LOG_DEBUG_VERBOSE,SC_SUCCESS); } static int entersafe_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 *kinfo = (sc_pkcs15_prkey_info_t *) obj->data; sc_card_t *card = p15card->card; sc_entersafe_wkey_data data; sc_file_t *tfile; const sc_acl_entry_t *acl_entry; int r; struct sc_pkcs15_prkey_info *key_info = (struct sc_pkcs15_prkey_info *)obj->data; size_t keybits = key_info->modulus_length; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if ( key->algorithm != SC_ALGORITHM_RSA ) { /* ignore non-RSA keys */ SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,SC_ERROR_INVALID_ARGUMENTS); } /* Disable RSA:512bits */ if ( ( keybits < 1024 ) || ( keybits > 2048 ) || ( keybits % 0x20 ) ) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unsupported key size %"SC_FORMAT_LEN_SIZE_T"u\n", keybits); return SC_ERROR_INVALID_ARGUMENTS; } r = sc_profile_get_file(profile, "PKCS15-AODF", &tfile); if (r < 0) return r; acl_entry = sc_file_get_acl_entry(tfile, SC_AC_OP_UPDATE); if (acl_entry->method != SC_AC_NONE) { r = sc_pkcs15init_authenticate(profile, p15card, tfile, SC_AC_OP_UPDATE); if(r<0) r = SC_ERROR_SECURITY_STATUS_NOT_SATISFIED; } sc_file_free(tfile); LOG_TEST_RET(card->ctx, r, "can't verify pin"); data.key_id = (u8) kinfo->key_reference; data.usage=0x22; data.key_data.rsa=&key->u.rsa; return sc_card_ctl(card, SC_CARDCTL_ENTERSAFE_WRITE_KEY, &data); } static int entersafe_generate_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *obj, sc_pkcs15_pubkey_t *pubkey) { int r; sc_entersafe_gen_key_data gendat; sc_pkcs15_prkey_info_t *kinfo = (sc_pkcs15_prkey_info_t *) obj->data; sc_card_t *card = p15card->card; sc_file_t *tfile; const sc_acl_entry_t *acl_entry; struct sc_pkcs15_prkey_info *key_info = (struct sc_pkcs15_prkey_info *)obj->data; size_t keybits = key_info->modulus_length; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if ( obj->type != SC_PKCS15_TYPE_PRKEY_RSA ) { return SC_ERROR_NOT_SUPPORTED; } /* Disable RSA:512bits */ if ( ( keybits < 1024 ) || ( keybits > 2048 ) || ( keybits % 0x20 ) ) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unsupported key size %"SC_FORMAT_LEN_SIZE_T"u\n", keybits); return SC_ERROR_INVALID_ARGUMENTS; } r = sc_profile_get_file(profile, "PKCS15-AODF", &tfile); if (r < 0) return r; acl_entry = sc_file_get_acl_entry(tfile, SC_AC_OP_UPDATE); if (acl_entry->method != SC_AC_NONE) { r = sc_pkcs15init_authenticate(profile, p15card, tfile, SC_AC_OP_UPDATE); if(r<0) r = SC_ERROR_SECURITY_STATUS_NOT_SATISFIED; } sc_file_free(tfile); 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); LOG_TEST_RET(card->ctx, r, "EnterSafe generate RSA key pair failed"); /* get the modulus via READ PUBLIC KEY */ if (pubkey) { u8 *buf; struct sc_pkcs15_pubkey_rsa *rsa = &pubkey->u.rsa; /* set the modulus */ rsa->modulus.data = gendat.modulus; rsa->modulus.len = kinfo->modulus_length >> 3; /* set the exponent (always 0x10001) */ buf = malloc(3); if (!buf) return SC_ERROR_OUT_OF_MEMORY; buf[0] = 0x01; buf[1] = 0x00; buf[2] = 0x01; rsa->exponent.data = buf; rsa->exponent.len = 3; pubkey->algorithm = SC_ALGORITHM_RSA; } else /* free public key */ free(gendat.modulus); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,SC_SUCCESS); } 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 = {0}; struct sc_pkcs15_object *objs[32]; int rv, nn, ii, update_df = 0; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); sc_log(ctx, "Check and if needed update PinFlags"); rv = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_AUTH_PIN, objs, 32); LOG_TEST_RET(ctx, rv, "Failed to get PINs"); nn = rv; sc_profile_get_pin_info(profile, SC_PKCS15INIT_USER_PIN, &profile_auth); LOG_TEST_RET(ctx, rv, "Failed to get PIN info"); for (ii=0; iidata; struct sc_pkcs15_pin_attributes *pin_attrs = &ainfo->attrs.pin; if (ainfo->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) continue; if (pin_attrs->reference == profile_auth.attrs.pin.reference && pin_attrs->flags != profile_auth.attrs.pin.flags) { 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; update_df = 1; } } if (update_df) { struct sc_pkcs15_df *df = p15card->df_list; while (df != NULL && df->type != SC_PKCS15_AODF) df = df->next; if (!df) LOG_TEST_RET(ctx, SC_ERROR_OBJECT_NOT_FOUND, "Cannot find AODF"); rv = sc_pkcs15init_update_any_df(p15card, profile, df, 0); LOG_TEST_RET(ctx, rv, "Update AODF error"); } SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, rv); } static struct sc_pkcs15init_operations sc_pkcs15init_entersafe_operations = { entersafe_erase_card, entersafe_init_card, entersafe_create_dir, NULL, /* create_domain */ entersafe_pin_reference, entersafe_create_pin, entersafe_key_reference, entersafe_create_key, entersafe_store_key, entersafe_generate_key, NULL, NULL, /* encode private/public key */ NULL, /* finalize */ NULL, /* delete_object */ NULL, NULL, NULL, NULL, NULL, /* pkcs15init emulation */ entersafe_sanity_check, }; struct sc_pkcs15init_operations *sc_pkcs15init_get_entersafe_ops(void) { return &sc_pkcs15init_entersafe_operations; } OpenSC-0.26.1/src/pkcs15init/pkcs15-epass2003.c000066400000000000000000000567531474147347300204360ustar00rootroot00000000000000/* * Support for ePass2003 smart cards * * Copyright (C) 2008, Weitao Sun * Copyright (C) 2011, Xiaoshuo Wu * * 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 "libopensc/log.h" #include "libopensc/opensc.h" #include "libopensc/cardctl.h" #include "libopensc/cards.h" #include "pkcs15-init.h" #include "profile.h" static int epass2003_pkcs15_erase_card(struct sc_profile *profile, struct sc_pkcs15_card *p15card) { SC_FUNC_CALLED(p15card->card->ctx, SC_LOG_DEBUG_VERBOSE); if (sc_select_file(p15card->card, sc_get_mf_path(), NULL) < 0) return SC_SUCCESS; return sc_card_ctl(p15card->card, SC_CARDCTL_ERASE_CARD, 0); } static int epass2003_pkcs15_init_card(struct sc_profile *profile, struct sc_pkcs15_card *p15card) { struct sc_card *card = p15card->card; int ret; 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 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); LOG_TEST_RET(card->ctx, ret, "Get MF info failed"); ret = sc_create_file(card, mf_file); sc_file_free(mf_file); LOG_TEST_RET(card->ctx, ret, "Create MF failed"); ret = sc_profile_get_file(profile, "SKey-MF", &skey_file); 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 failed"); } { /* EF(DIR) */ struct sc_file *dir_file; /* get dir profile */ ret = sc_profile_get_file(profile, "DIR", &dir_file); LOG_TEST_RET(card->ctx, ret, "Get EF(DIR) info failed"); ret = sc_create_file(card, dir_file); sc_file_free(dir_file); LOG_TEST_RET(card->ctx, ret, "Create EF(DIR) failed"); sc_free_apps(card); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); } static int epass2003_pkcs15_create_dir(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_file *df) { struct sc_card *card = p15card->card; int ret; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); { /* p15 DF */ struct sc_file *df_file; struct sc_file *skey_file; struct sc_file *ef_file; u8 max_counter[2] = { 0 }; int id; u8 user_maxtries = 0; u8 so_maxtries = 0; ret = sc_profile_get_file(profile, "PKCS15-AppDF", &df_file); LOG_TEST_RET(card->ctx, ret, "Get PKCS15-AppDF info failed"); ret = sc_create_file(card, df_file); sc_file_free(df_file); LOG_TEST_RET(card->ctx, ret, "Create PKCS15-AppDF failed"); ret = sc_profile_get_file(profile, "SKey-AppDF", &skey_file); 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); LOG_TEST_RET(card->ctx, ret, "Get MAXPIN info failed"); ret = sc_create_file(card, ef_file); LOG_TEST_RET(card->ctx, ret, "Create MAXPIN failed"); ret = sc_select_file(card, &(ef_file->path), &ef_file); LOG_TEST_RET(card->ctx, ret, "Select MAXPIN failed"); ret = sc_profile_get_pin_id(profile, 2, &id); 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); LOG_TEST_RET(card->ctx, ret, "Get User PIN id error!"); so_maxtries = (u8) sc_profile_get_pin_retries(profile, id); max_counter[0] = user_maxtries; max_counter[1] = so_maxtries; ret = sc_update_binary(card, 0, max_counter, 2, 0); LOG_TEST_RET(card->ctx, ret, "Update MAXPIN failed"); sc_file_free(ef_file); } { /* p15 efs */ char *create_efs[] = { "PKCS15-ODF", "PKCS15-TokenInfo", "PKCS15-UnusedSpace", "PKCS15-AODF", "PKCS15-PrKDF", "PKCS15-PuKDF", "PKCS15-CDF", "PKCS15-DODF", NULL, }; int i; struct sc_file *file = 0; for (i = 0; create_efs[i]; ++i) { if (sc_profile_get_file(profile, create_efs[i], &file)) { 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); } ret = sc_create_file(card, file); sc_file_free(file); LOG_TEST_RET(card->ctx, ret, "Create pkcs15 file failed"); } } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, ret); } static int epass2003_pkcs15_pin_reference(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_pkcs15_auth_info *auth_info) { 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 (auth_info->attrs.pin.reference < ENTERSAFE_USER_PIN_ID || auth_info->attrs.pin.reference > ENTERSAFE_SO_PIN_ID) return SC_ERROR_INVALID_PIN_REFERENCE; SC_FUNC_RETURN(p15card->card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); } static int epass2003_pkcs15_create_pin(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_file *df, struct sc_pkcs15_object *pin_obj, const unsigned char *pin, size_t pin_len, const unsigned char *puk, size_t puk_len) { struct sc_card *card = p15card->card; int r; struct sc_pkcs15_auth_info *auth_info; if (NULL == pin_obj) return SC_ERROR_INVALID_ARGUMENTS; auth_info = (struct sc_pkcs15_auth_info *)pin_obj->data; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return SC_ERROR_OBJECT_NOT_VALID; { /*pin */ sc_epass2003_wkey_data data; int id; if (!pin || !pin_len || pin_len > 16) return SC_ERROR_INVALID_ARGUMENTS; data.type = SC_EPASS2003_SECRET_PIN; data.key_data.es_secret.kid = auth_info->attrs.pin.reference; data.key_data.es_secret.ac[0] = EPASS2003_AC_MAC_NOLESS | EPASS2003_AC_EVERYONE; data.key_data.es_secret.ac[1] = EPASS2003_AC_MAC_NOLESS | EPASS2003_AC_USER; r = sc_profile_get_pin_id(profile, 2, &id); LOG_TEST_RET(card->ctx, r, "Get User PIN id error!"); data.key_data.es_secret.EC = sc_profile_get_pin_retries(profile, id); /* pad pin with 0 */ memset(data.key_data.es_secret.key_val, 0, sizeof(data.key_data.es_secret.key_val)); memcpy(data.key_data.es_secret.key_val, pin, pin_len); 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); } } { /*puk */ sc_epass2003_wkey_data data; int id; if (!puk || !puk_len || puk_len > 16) return SC_ERROR_INVALID_ARGUMENTS; data.type = SC_EPASS2003_SECRET_PIN; data.key_data.es_secret.kid = auth_info->attrs.pin.reference + 1; data.key_data.es_secret.ac[0] = EPASS2003_AC_MAC_NOLESS | EPASS2003_AC_EVERYONE; data.key_data.es_secret.ac[1] = EPASS2003_AC_MAC_EQUAL | EPASS2003_AC_SO; r = sc_profile_get_pin_id(profile, 1, &id); LOG_TEST_RET(card->ctx, r, "Get User PIN id error!"); data.key_data.es_secret.EC = sc_profile_get_pin_retries(profile, id); /* pad pin with 0 */ memset(data.key_data.es_secret.key_val, 0, sizeof(data.key_data.es_secret.key_val)); memcpy(data.key_data.es_secret.key_val, puk, puk_len); data.key_data.es_secret.key_len = puk_len; r = sc_card_ctl(card, SC_CARDCTL_ENTERSAFE_WRITE_KEY, &data); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } static int epass2003_pkcs15_key_reference(struct sc_profile *profile, struct sc_pkcs15_card *p15card, 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); } /* from pkcs15-oberthur.c, modified */ 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 = NULL; const char *_template = NULL, *desc = NULL; unsigned int structure = 0xFFFFFFFF; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); 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"; structure = SC_CARDCTL_OBERTHUR_KEY_RSA_CRT; break; case SC_PKCS15_TYPE_PUBKEY_RSA: desc = "RSA public key"; _template = "public-key"; structure = SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC; break; case SC_PKCS15_TYPE_PRKEY: desc = "extractable private key"; _template = "extractable-key"; break; case SC_PKCS15_TYPE_CERT: desc = "certificate"; _template = "certificate"; break; case SC_PKCS15_TYPE_DATA_OBJECT: desc = "data object"; _template = "data"; break; } if (_template) 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_log(card->ctx, "File type %X not supported by card driver", type); return SC_ERROR_INVALID_ARGUMENTS; } type &= SC_PKCS15_TYPE_CLASS_MASK; } sc_log(card->ctx, "template %s; num %i\n", _template, num); if (sc_profile_get_file(profile, _template, &file) < 0) { 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); file->path.value[file->path.len - 1] = (num & 0xFF); file->type = SC_FILE_TYPE_INTERNAL_EF; file->ef_structure = structure; 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_log(card->ctx, "file path: %s", sc_print_path(&(file->path))); *out = file; SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); } static int epass2003_pkcs15_create_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *obj) { struct sc_card *card = p15card->card; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); } static int epass2003_pkcs15_store_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *obj, struct sc_pkcs15_prkey *key) { struct sc_card *card = p15card->card; struct sc_pkcs15_prkey_info *key_info = (struct sc_pkcs15_prkey_info *)obj->data; size_t idx = key_info->key_reference; size_t keybits = key_info->modulus_length; struct sc_path path; struct sc_file *tfile = NULL; struct sc_file *file = NULL; sc_epass2003_wkey_data data; int r; int fidl = 0; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); 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) LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED, "store key: only support RSA"); 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)); /* allocate key object */ r = cosm_new_file(profile, card, SC_PKCS15_TYPE_PRKEY_RSA, key_info->key_reference, &file); LOG_TEST_RET(card->ctx, r, "create key: failed to allocate new key object"); file->size = keybits; sc_log(card->ctx, "private key path: %s", sc_print_path(&(file->path))); sc_log(card->ctx, "private key_info path: %s", sc_print_path(&(key_info->path))); sc_delete_file(p15card->card, &file->path); /* create */ r = sc_pkcs15init_create_file(profile, p15card, file); LOG_TEST_RET(card->ctx, r, "create key: failed to create key file"); 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)) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL, "Unsupported key size %"SC_FORMAT_LEN_SIZE_T"u\n", keybits); return SC_ERROR_INVALID_ARGUMENTS; } path = key_info->path; path.len -= 2; r = sc_select_file(card, &path, &tfile); LOG_TEST_RET(card->ctx, r, "generate key: no private object DF"); r = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_UPDATE); LOG_TEST_RET(card->ctx, r, "No authorisation to store private key"); sc_file_free(tfile); fidl = (file->id & 0xff) * FID_STEP; file->id = (file->id & 0xff00) + fidl; data.type = SC_EPASS2003_KEY_RSA; data.key_data.es_key.fid = file->id; data.key_data.es_key.rsa = (void *)&key->u.rsa; r = sc_card_ctl(p15card->card, SC_CARDCTL_ENTERSAFE_WRITE_KEY, &data); LOG_TEST_RET(card->ctx, r, "store key: cannot update private key"); sc_file_free(file); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } static int epass2003_pkcs15_generate_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *obj, struct sc_pkcs15_pubkey *pubkey) { struct sc_card *card = p15card->card; int r; sc_epass2003_gen_key_data gendat; struct sc_pkcs15_prkey_info *key_info = (struct sc_pkcs15_prkey_info *)obj->data; int idx = key_info->key_reference; size_t keybits = key_info->modulus_length; struct sc_file *tfile = NULL, *pukf = NULL; struct sc_path path; struct sc_file *file = NULL; int fidl = 0; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); 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, 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_log(card->ctx, "private key path: %s", sc_print_path(&file->path)); 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_VERBOSE, r, "generate key: pkcs15init_authenticate(SC_AC_OP_DELETE) failed"); 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_VERBOSE, r, "create key: failed to create key file"); sc_log(card->ctx, "index %u; keybits %"SC_FORMAT_LEN_SIZE_T"u\n", idx, keybits); if (keybits < 1024 || keybits > 2048 || (keybits % 0x20)) { 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_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_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_VERBOSE, r, "generate key: pkcs15init_authenticate(SC_AC_OP_CREATE) failed"); 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_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)); r = sc_select_file(p15card->card, &pukf->path, NULL); /* if exist, delete */ if (r == SC_SUCCESS) { r = sc_pkcs15init_authenticate(profile, p15card, pukf, SC_AC_OP_DELETE); 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_log(card->ctx, "generate key: failed to delete existing key file\n"); goto err; } } /* create */ r = sc_pkcs15init_create_file(profile, p15card, pukf); if (r != SC_SUCCESS) { 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_VERBOSE, r, "generate key - pubkey: pkcs15init_authenticate(SC_AC_OP_UPDATE) failed"); /* generate key pair */ fidl = (file->id & 0xff) * FID_STEP; file->id = (file->id & 0xff00) + fidl; pukf->id = (pukf->id & 0xff00) + fidl; gendat.prkey_id = file->id; 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_VERBOSE, r, "generate RSA key pair failed"); if (!gendat.modulus) { r = SC_ERROR_OUT_OF_MEMORY; goto err; } /* get the modulus */ if (pubkey && (obj->type == SC_PKCS15_TYPE_PRKEY_RSA)) { u8 *buf; struct sc_pkcs15_pubkey_rsa *rsa = &pubkey->u.rsa; /* set the modulus */ rsa->modulus.data = gendat.modulus; rsa->modulus.len = keybits >> 3; /* set the exponent (always 0x10001) */ buf = (u8 *) malloc(3); if (!buf) { r = SC_ERROR_OUT_OF_MEMORY; goto err; } buf[0] = 0x01; buf[1] = 0x00; buf[2] = 0x01; rsa->exponent.data = buf; rsa->exponent.len = 3; pubkey->algorithm = SC_ALGORITHM_RSA; } 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); err: sc_file_free(pukf); 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); } static int epass2003_pkcs15_delete_object(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *object, const struct sc_path *path) { SC_FUNC_CALLED(p15card->card->ctx, SC_LOG_DEBUG_VERBOSE); return sc_pkcs15init_delete_by_path(profile, p15card, path); } 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 = {0}; struct sc_pkcs15_object *objs[32]; int rv, nn, ii, update_df = 0; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); sc_log(ctx, "Check and if needed update PinFlags"); rv = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_AUTH_PIN, objs, 32); LOG_TEST_RET(ctx, rv, "Failed to get PINs"); nn = rv; sc_profile_get_pin_info(profile, SC_PKCS15INIT_USER_PIN, &profile_auth); LOG_TEST_RET(ctx, rv, "Failed to get PIN info"); for (ii = 0; ii < nn; ii++) { struct sc_pkcs15_auth_info *ainfo = (struct sc_pkcs15_auth_info *)objs[ii]->data; struct sc_pkcs15_pin_attributes *pin_attrs = &ainfo->attrs.pin; if (ainfo->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) continue; if (pin_attrs->reference == profile_auth.attrs.pin.reference && pin_attrs->flags != profile_auth.attrs.pin.flags) { 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; update_df = 1; } } if (update_df) { struct sc_pkcs15_df *df = p15card->df_list; while (df != NULL && df->type != SC_PKCS15_AODF) df = df->next; if (!df) LOG_TEST_RET(ctx, SC_ERROR_OBJECT_NOT_FOUND, "Cannot find AODF"); rv = sc_pkcs15init_update_any_df(p15card, profile, df, 0); LOG_TEST_RET(ctx, rv, "Update AODF error"); } SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, rv); } static struct sc_pkcs15init_operations sc_pkcs15init_epass2003_operations = { epass2003_pkcs15_erase_card, epass2003_pkcs15_init_card, epass2003_pkcs15_create_dir, NULL, /* create_domain */ epass2003_pkcs15_pin_reference, epass2003_pkcs15_create_pin, epass2003_pkcs15_key_reference, epass2003_pkcs15_create_key, epass2003_pkcs15_store_key, epass2003_pkcs15_generate_key, NULL, NULL, /* encode private/public key */ NULL, /* finalize */ epass2003_pkcs15_delete_object, NULL, NULL, NULL, NULL, NULL, /* pkcs15init emulation */ epass2003_pkcs15_sanity_check, }; struct sc_pkcs15init_operations *sc_pkcs15init_get_epass2003_ops(void) { return &sc_pkcs15init_epass2003_operations; } OpenSC-0.26.1/src/pkcs15init/pkcs15-gids.c000066400000000000000000000150321474147347300177250ustar00rootroot00000000000000/* * pkcs15-gids.c: Support for GIDS smart cards. * * Copyright (C) 2015 Vincent Le Toux (My Smart Logon) * * 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 #include "../libopensc/log.h" #include "../libopensc/opensc.h" #include "../libopensc/cardctl.h" #include "../libopensc/asn1.h" #include "pkcs15-init.h" #include "profile.h" #include "../libopensc/card-gids.h" /* * Select a key reference. */ static int gids_select_key_reference(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_prkey_info_t *key_info) { sc_card_t *card = p15card->card; LOG_FUNC_RETURN(card->ctx, sc_card_ctl(card, SC_CARDCTL_GIDS_SELECT_KEY_REFERENCE, key_info)); } /* * Create a new key file. */ static int gids_create_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *obj) { sc_card_t *card = p15card->card; LOG_FUNC_RETURN(card->ctx, sc_card_ctl(card, SC_CARDCTL_GIDS_CREATE_KEY, obj)); } /* * Generate a new key. */ static int gids_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; struct sc_cardctl_gids_genkey call = {obj, pubkey}; LOG_FUNC_RETURN(card->ctx, sc_card_ctl(card, SC_CARDCTL_GIDS_GENERATE_KEY, &call)); } /* * Store a usable private key on the card. */ static int gids_store_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *object, sc_pkcs15_prkey_t *key) { sc_card_t *card = p15card->card; struct sc_cardctl_gids_importkey call = {object, key}; LOG_FUNC_RETURN(card->ctx, sc_card_ctl(card, SC_CARDCTL_GIDS_IMPORT_KEY, &call)); } static int gids_delete_object(struct sc_profile *profile, struct sc_pkcs15_card * p15card, struct sc_pkcs15_object *object, const struct sc_path *path) { sc_card_t *card = p15card->card; switch(object->type & SC_PKCS15_TYPE_CLASS_MASK) { case SC_PKCS15_TYPE_PRKEY: LOG_FUNC_RETURN(card->ctx, sc_card_ctl(card, SC_CARDCTL_GIDS_DELETE_KEY, object)); break; case SC_PKCS15_TYPE_CERT: LOG_FUNC_RETURN(card->ctx, sc_card_ctl(card, SC_CARDCTL_GIDS_DELETE_CERT, object)); break; case SC_PKCS15_TYPE_PUBKEY: LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); default: LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } } static int gids_emu_update_any_df(struct sc_profile *profile, struct sc_pkcs15_card *p15card, unsigned op, struct sc_pkcs15_object *object) { LOG_FUNC_CALLED(p15card->card->ctx); /* After storing object, pkcs15init will call this function to update DF. * But GIDS has no other DF than GIDS-Application, so we do nothing. */ LOG_FUNC_RETURN(p15card->card->ctx, SC_SUCCESS); } static int gids_save_certificate(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *object, struct sc_path *path) { int r; sc_card_t *card = p15card->card; struct sc_cardctl_gids_save_cert call = {object, NULL, path}; struct sc_pkcs15_cert_info *cert_info = (struct sc_pkcs15_cert_info *) object->data; r = sc_pkcs15_find_object_by_id(p15card, SC_PKCS15_TYPE_PRKEY, &cert_info->id , &(call.privkeyobject)); if (r == SC_ERROR_OBJECT_NOT_FOUND) { //TODO save the certificate in the special file LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } 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)); } static int gids_emu_store_data(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15_object *object, struct sc_pkcs15_der *content, struct sc_path *path) { sc_card_t *card = p15card->card; int r; LOG_FUNC_CALLED(card->ctx); switch (object->type & SC_PKCS15_TYPE_CLASS_MASK) { case SC_PKCS15_TYPE_PRKEY: case SC_PKCS15_TYPE_PUBKEY: /* For these two type, store_data just don't need to do anything. * All have been done already before this function is called */ r = SC_SUCCESS; break; case SC_PKCS15_TYPE_CERT: r = gids_save_certificate(p15card, object, path); break; default: r = SC_ERROR_NOT_IMPLEMENTED; break; } LOG_FUNC_RETURN(card->ctx, r); } static int gids_emu_update_tokeninfo(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_tokeninfo_t *tokeninfo) { LOG_FUNC_CALLED(p15card->card->ctx); /* When unbinding pkcs15init, this function will be called. * But for GIDS, token info does not need to change, we do nothing. */ LOG_FUNC_RETURN(p15card->card->ctx, SC_SUCCESS); } static struct sc_pkcs15init_operations sc_pkcs15init_gids_operations = { NULL, /* erase_card */ NULL, /* init_card */ NULL, /* create_dir */ NULL, /* create_domain */ NULL, /* pin_reference*/ NULL, /* create_pin */ gids_select_key_reference, /* key_reference */ gids_create_key, /* create_key */ gids_store_key, /* store_key */ gids_generate_key, /* generate_key */ NULL, NULL, /* encode private/public key */ NULL, /* finalize */ gids_delete_object, /* delete_object */ NULL, /* pkcs15init emulation emu_update_dir */ gids_emu_update_any_df, /* pkcs15init emulation emu_update_any_df */ gids_emu_update_tokeninfo, /* pkcs15init emulation emu_update_tokeninfo */ NULL, /* pkcs15init emulation emu_write_info */ gids_emu_store_data, /* pkcs15init emulation emu_store_data */ NULL, /* sanity_check*/ }; struct sc_pkcs15init_operations *sc_pkcs15init_get_gids_ops(void) { return &sc_pkcs15init_gids_operations; } OpenSC-0.26.1/src/pkcs15init/pkcs15-iasecc.c000066400000000000000000001756441474147347300202460ustar00rootroot00000000000000/* * IAS/ECC specific operations for PKCS #15 initialization * * Copyright (C) 2002 Juha Yrjölä * Copyright (C) 2010 Viktor Tarasov * OpenTrust * * 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 #ifndef FIX_UNUSED #define FIX_UNUSED(X) (void) (X) /* avoid warnings for unused params */ #endif #ifdef ENABLE_OPENSSL /* empty file without openssl */ #include #include #include #include #include #include #include "../libopensc/opensc.h" #include "../libopensc/cardctl.h" #include "../libopensc/log.h" #include "../libopensc/pkcs15.h" #include "../libopensc/cards.h" #include "../libopensc/iasecc.h" #include "../libopensc/iasecc-sdo.h" #include "pkcs15-init.h" #include "profile.h" #define IASECC_TITLE "IASECC" int iasecc_pkcs15_delete_file(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_file *df); static int iasecc_md_gemalto_delete_prvkey(struct sc_pkcs15_card *, struct sc_profile *, struct sc_pkcs15_object *); static void iasecc_reference_to_pkcs15_id (unsigned int ref, struct sc_pkcs15_id *id) { int ii, sz; for (ii=0, sz = 0; (unsigned)ii < sizeof(unsigned int); ii++) if (ref >> 8*ii) sz++; for (ii=0; ii < sz; ii++) id->value[sz - ii - 1] = (ref >> 8*ii) & 0xFF; id->len = sz; } int iasecc_pkcs15_delete_file(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_file *df) { struct sc_context *ctx = p15card->card->ctx; struct sc_card *card = p15card->card; struct sc_path path; unsigned long caps = card->caps; int rv = 0; LOG_FUNC_CALLED(ctx); sc_log(ctx, "iasecc_pkcs15_delete_file() id %04X\n", df->id); card->caps |= SC_CARD_CAP_USE_FCI_AC; rv = sc_pkcs15init_authenticate(profile, p15card, df, SC_AC_OP_DELETE); card->caps = caps; LOG_TEST_RET(ctx, rv, "Cannot authenticate SC_AC_OP_DELETE"); memset(&path, 0, sizeof(path)); path.type = SC_PATH_TYPE_FILE_ID; path.value[0] = df->id >> 8; path.value[1] = df->id & 0xFF; path.len = 2; rv = sc_delete_file(card, &path); LOG_FUNC_RETURN(ctx, rv); } /* * Erase the card * */ static int iasecc_pkcs15_erase_card(struct sc_profile *profile, struct sc_pkcs15_card *p15card) { struct sc_context *ctx = p15card->card->ctx; struct sc_file *file = NULL; struct sc_path path; struct sc_pkcs15_df *df; int rv; LOG_FUNC_CALLED(ctx); 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); path.len = p15card->app->ddo.aid.len; sc_log(ctx, "Select DDO AID: %s", sc_print_path(&path)); rv = sc_select_file(p15card->card, &path, NULL); LOG_TEST_RET(ctx, rv, "Erase application error: cannot select DDO AID"); } for (df = p15card->df_list; df; df = df->next) { struct sc_pkcs15_object *objs[32]; unsigned obj_type = 0; int ii; if (df->type == SC_PKCS15_PRKDF) obj_type = SC_PKCS15_TYPE_PRKEY; else if (df->type == SC_PKCS15_PUKDF) obj_type = SC_PKCS15_TYPE_PUBKEY; else if (df->type == SC_PKCS15_CDF) obj_type = SC_PKCS15_TYPE_CERT; else if (df->type == SC_PKCS15_DODF) obj_type = SC_PKCS15_TYPE_DATA_OBJECT; else continue; 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; iidata))->path; rv = sc_delete_file(p15card->card, &path); } else if (obj_type == SC_PKCS15_TYPE_DATA_OBJECT) { struct sc_path path = ((struct sc_pkcs15_data_info *)(objs[ii]->data))->path; rv = sc_delete_file(p15card->card, &path); } sc_pkcs15_remove_object(p15card, objs[ii]); sc_pkcs15_free_object(objs[ii]); } rv = sc_select_file(p15card->card, &df->path, &file); if (rv == SC_ERROR_FILE_NOT_FOUND) continue; LOG_TEST_RET(ctx, rv, "Cannot select object file"); profile->dirty = 1; 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); LOG_TEST_RET(ctx, rv, "SC_AC_OP_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_FUNC_RETURN(ctx, SC_SUCCESS); } /* * Allocate a file */ static int iasecc_pkcs15_new_file(struct sc_profile *profile, struct sc_card *card, unsigned int type, unsigned int num, struct sc_file **out) { struct sc_context *ctx = card->ctx; struct sc_file *file = NULL; const char *_template = NULL; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "type %X; num %i\n", type, num); switch (type) { case SC_PKCS15_TYPE_PRKEY_RSA: _template = "private-key"; break; case SC_PKCS15_TYPE_PUBKEY_RSA: _template = "public-key"; break; case SC_PKCS15_TYPE_CERT: _template = "certificate"; break; case SC_PKCS15_TYPE_DATA_OBJECT: _template = "public-data"; break; default: LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Profile template not supported"); } sc_log(ctx, "df_info path '%s'\n", sc_print_path(&profile->df_info->file->path)); rv = sc_profile_get_file(profile, _template, &file); if (rv == SC_ERROR_FILE_NOT_FOUND) { struct sc_pkcs15_id id; id.len = 1; id.value[0] = num & 0xFF; rv = sc_profile_instantiate_template(profile, "key-domain", &profile->df_info->file->path, _template, &id, &file); } LOG_TEST_RET(ctx, rv, "Error when getting file from template"); sc_log(ctx, "path(type:%X;path:%s)\n", file->path.type, sc_print_path(&file->path)); file->id = (file->id & 0xFF00) | (num & 0xFF); if (file->path.len == 0) { file->path.type = SC_PATH_TYPE_FILE_ID; file->path.len = 2; } file->path.value[file->path.len - 2] = (file->id >> 8) & 0xFF; file->path.value[file->path.len - 1] = file->id & 0xFF; file->path.count = -1; sc_log(ctx, "file size %"SC_FORMAT_LEN_SIZE_T"u; ef type %i/%i; id %04X\n", file->size, file->type, file->ef_structure, file->id); sc_log(ctx, "path type %X; path '%s'", file->path.type, sc_print_path(&file->path)); if (out) *out = file; else sc_file_free(file); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } /* * Select a key reference */ static int iasecc_pkcs15_select_key_reference(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_pkcs15_prkey_info *key_info) { struct sc_context *ctx = p15card->card->ctx; struct sc_card *card = p15card->card; struct sc_file *file = NULL; int rv = 0, idx = key_info->key_reference & ~IASECC_OBJECT_REF_LOCAL; LOG_FUNC_CALLED(ctx); sc_log(ctx, "'seed' key reference %i; path %s", key_info->key_reference & ~IASECC_OBJECT_REF_LOCAL, 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 */ if (idx < IASECC_OBJECT_REF_MIN) idx = IASECC_OBJECT_REF_MIN; /* Look for the suitable slot */ if (idx <= IASECC_OBJECT_REF_MAX) { struct iasecc_ctl_get_free_reference ctl_data; ctl_data.key_size = key_info->modulus_length; ctl_data.usage = key_info->usage; ctl_data.access = key_info->access_flags; ctl_data.index = idx; rv = sc_card_ctl(card, SC_CARDCTL_IASECC_GET_FREE_KEY_REFERENCE, &ctl_data); if (!rv) sc_log(ctx, "found allocated slot %i", idx); else if (rv == SC_ERROR_DATA_OBJECT_NOT_FOUND && idx <= IASECC_OBJECT_REF_MAX) sc_log(ctx, "found empty slot %i", idx); else LOG_TEST_RET(ctx, rv, "Cannot select key reference"); idx = ctl_data.index; } /* All card objects but PINs are locals */ key_info->key_reference = idx | IASECC_OBJECT_REF_LOCAL; sc_log(ctx, "selected key reference %i", key_info->key_reference); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int iasecc_sdo_get_data(struct sc_card *card, struct iasecc_sdo *sdo) { struct sc_context *ctx = card->ctx; int rv; LOG_FUNC_CALLED(ctx); rv = sc_card_ctl(card, SC_CARDCTL_IASECC_SDO_GET_DATA, sdo); LOG_TEST_RET(ctx, rv, "IasEcc: GET DATA error"); LOG_FUNC_RETURN(ctx, rv); } static int iasecc_file_convert_acls(struct sc_context *ctx, struct sc_profile *profile, struct sc_file *file) { int ii; for (ii=0; iimethod) { case SC_AC_IDA: 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; acl->key_ref &= IASECC_SCB_METHOD_MASK_REF; } else if ((acl->key_ref & IASECC_SCB_METHOD_MASK) == IASECC_SCB_METHOD_SM) { acl->method = SC_AC_PRO; acl->key_ref &= IASECC_SCB_METHOD_MASK_REF; } } } } return 0; } static int iasecc_sdo_set_key_acls_from_profile(struct sc_profile *profile, struct sc_card *card, const char *template, struct iasecc_sdo *sdo) { struct sc_context *ctx = card->ctx; struct sc_file *file = NULL; unsigned char ops_prvkey[7] = { SC_AC_OP_PSO_COMPUTE_SIGNATURE, SC_AC_OP_INTERNAL_AUTHENTICATE, SC_AC_OP_PSO_DECRYPT, SC_AC_OP_GENERATE, 0xFF, SC_AC_OP_UPDATE, SC_AC_OP_READ }; unsigned char ops_pubkey[7] = { 0xFF, SC_AC_OP_EXTERNAL_AUTHENTICATE, 0xFF, SC_AC_OP_GENERATE, 0xFF, SC_AC_OP_UPDATE, SC_AC_OP_READ }; unsigned char amb, scb[16], mask; int rv, ii, cntr; LOG_FUNC_CALLED(ctx); /* Get ACLs from profile template */ rv = sc_profile_get_file(profile, template, &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); if (rv < 0) sc_file_free(file); LOG_TEST_RET(ctx, rv, "Cannot convert profile ACLs"); memset(scb, 0, sizeof(scb)); for (ii = 0, mask = 0x80, amb = 0x80, cntr = 0; ii < 7; ii++) { const sc_acl_entry_t *acl; unsigned char op = sdo->sdo_class == IASECC_SDO_CLASS_RSA_PRIVATE ? ops_prvkey[ii] : ops_pubkey[ii]; mask >>= 1; if (op == 0xFF) continue; acl = sc_file_get_acl_entry(file, op); sc_log(ctx, "ACL: 0x%X:0x%X", acl->method, acl->key_ref); if (acl->method == SC_AC_NEVER) { } else if (acl->method == SC_AC_NONE) { amb |= mask; scb[cntr++] = 0x00; } else if (acl->method == SC_AC_SEN || acl->method == SC_AC_PRO || acl->method == SC_AC_AUT) { if ((acl->key_ref & 0xF) == 0 || (acl->key_ref & 0xF) == 0xF) { sc_file_free(file); LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Invalid SE reference"); } amb |= mask; if (acl->method == SC_AC_SEN) scb[cntr++] = acl->key_ref | IASECC_SCB_METHOD_USER_AUTH; else if (acl->method == SC_AC_PRO) scb[cntr++] = acl->key_ref | IASECC_SCB_METHOD_SM; else scb[cntr++] = acl->key_ref | IASECC_SCB_METHOD_EXT_AUTH; } else { sc_file_free(file); LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Unknown SCB method"); } } sc_file_free(file); /* Copy ACLs into the DOCP*/ sdo->docp.acls_contact.tag = IASECC_DOCP_TAG_ACLS_CONTACT; sdo->docp.acls_contact.size = cntr + 1; sdo->docp.acls_contact.value = calloc(1, sdo->docp.acls_contact.size); if (!sdo->docp.acls_contact.value) return SC_ERROR_OUT_OF_MEMORY; *(sdo->docp.acls_contact.value + 0) = amb; memcpy(sdo->docp.acls_contact.value + 1, scb, cntr); sc_log(ctx, "AMB: %X, CNTR %i, %x %x %x %x %x %x %x", amb, cntr, scb[0], scb[1], scb[2], scb[3], scb[4], scb[5], scb[6]); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int iasecc_sdo_allocate_prvkey(struct sc_profile *profile, struct sc_card *card, struct sc_pkcs15_prkey_info *key_info, struct iasecc_sdo **out) { struct sc_context *ctx = card->ctx; struct iasecc_sdo *sdo = NULL; size_t sz = key_info->modulus_length / 8; int rv; LOG_FUNC_CALLED(ctx); sdo = calloc(1, sizeof(struct iasecc_sdo)); if (!sdo) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot allocate 'iasecc_sdo'"); sdo->magic = SC_CARDCTL_IASECC_SDO_MAGIC; sdo->sdo_ref = key_info->key_reference & 0x3F; sdo->sdo_class = IASECC_SDO_CLASS_RSA_PRIVATE; sdo->usage = key_info->usage; sc_log(ctx, "sdo->sdo_class 0x%X; sdo->usage 0x%X", sdo->sdo_class, sdo->usage); rv = iasecc_sdo_get_data(card, sdo); if (rv == SC_ERROR_DATA_OBJECT_NOT_FOUND) { 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) { iasecc_sdo_free(card, sdo); LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); } 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) { 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) { 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; *(sdo->docp.size.value + 1) = sz & 0xFF; /* FIXME: Manage CRT key types: IASECC_GEN_KEY_TYPE_*: X509_usage Optional PRIVATE KEY SDO attribute 'Algorithm to compulsorily use' can have one of the three values: 0(any usage), B6(Sign), A4(Authentication), B8(Confidentiality). 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_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) && (key_info->usage & SC_PKCS15_PRKEY_USAGE_NONREPUDIATION)) { *(sdo->docp.non_repudiation.value + 0) = 1; *(sdo->data.prv_key.compulsory.value + 0) = IASECC_CRT_TAG_DST; } 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 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 iasecc_sdo_free(card, sdo); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int iasecc_sdo_allocate_pubkey(struct sc_profile *profile, struct sc_card *card, struct sc_pkcs15_pubkey_info *key_info, struct iasecc_sdo **out) { struct sc_context *ctx = card->ctx; struct iasecc_sdo *sdo = NULL; size_t sz = key_info->modulus_length / 8; int rv; LOG_FUNC_CALLED(ctx); sdo = calloc(1, sizeof(struct iasecc_sdo)); if (!sdo) return SC_ERROR_OUT_OF_MEMORY; sdo->magic = SC_CARDCTL_IASECC_SDO_MAGIC; sdo->sdo_ref = key_info->key_reference & 0x3F; sdo->sdo_class = IASECC_SDO_CLASS_RSA_PUBLIC; rv = iasecc_sdo_get_data(card, sdo); sc_log(ctx, "get Public Key SDO(class:%X) data returned %i", sdo->sdo_class, rv); if (rv == SC_ERROR_DATA_OBJECT_NOT_FOUND) { 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) { 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; *(sdo->docp.size.value + 1) = sz & 0xFF; if (card->type == SC_CARD_TYPE_IASECC_OBERTHUR) { /* TODO: Disabled for the tests of the Oberthur card */ } else { sdo->data.pub_key.cha.value = calloc(1, 2); 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) { 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 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 iasecc_sdo_free(card, sdo); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int iasecc_sdo_convert_to_file(struct sc_card *card, struct iasecc_sdo *sdo, struct sc_file **out) { struct sc_context *ctx; struct sc_file *file; unsigned ii; int rv; if (!card || !sdo) return SC_ERROR_INVALID_ARGUMENTS; ctx = card->ctx; LOG_FUNC_CALLED(ctx); file = sc_file_new(); if (!file) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); sc_log(ctx, "SDO class 0x%X", sdo->sdo_class); if (sdo->sdo_class == IASECC_SDO_CLASS_RSA_PRIVATE) { unsigned char ops[] = { SC_AC_OP_PSO_COMPUTE_SIGNATURE, SC_AC_OP_INTERNAL_AUTHENTICATE, SC_AC_OP_PSO_DECRYPT, SC_AC_OP_GENERATE, SC_AC_OP_UPDATE, SC_AC_OP_READ }; for (ii=0; iiaccess_rules[ii].access_mode) { object->access_rules[ii].access_mode = access_mode; if (auth_id) object->access_rules[ii].auth_id = *auth_id; else object->access_rules[ii].auth_id.len = 0; break; } else if (!auth_id && !object->access_rules[ii].auth_id.len) { object->access_rules[ii].access_mode |= access_mode; break; } else if (auth_id && sc_pkcs15_compare_id(&object->access_rules[ii].auth_id, auth_id)) { object->access_rules[ii].access_mode |= access_mode; break; } } if (ii==SC_PKCS15_MAX_ACCESS_RULES) return SC_ERROR_TOO_MANY_OBJECTS; return SC_SUCCESS; } static int iasecc_pkcs15_get_auth_id_from_se(struct sc_pkcs15_card *p15card, unsigned char scb, struct sc_pkcs15_id *auth_id) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_object *pin_objs[32]; int rv, ii, nn_pins, se_ref, pin_ref; LOG_FUNC_CALLED(ctx); if (!auth_id) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); memset(auth_id, 0, sizeof(struct sc_pkcs15_id)); if (!(scb & IASECC_SCB_METHOD_USER_AUTH)) LOG_FUNC_RETURN(ctx, SC_SUCCESS); rv = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_AUTH_PIN, pin_objs, 32); LOG_TEST_RET(ctx, rv, "Error while getting AUTH objects"); nn_pins = rv; se_ref = scb & 0x0F; rv = sc_card_ctl(p15card->card, SC_CARDCTL_GET_CHV_REFERENCE_IN_SE, (void *)(&se_ref)); LOG_TEST_RET(ctx, rv, "Card CTL error: cannot get CHV reference from SE"); pin_ref = rv; for (ii=0; iidata; if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) continue; sc_log(ctx, "PIN refs %i/%i", pin_ref, auth_info->attrs.pin.reference); if (pin_ref == ((auth_info->attrs.pin.reference + 0x100) % 0x100)) { *auth_id = auth_info->auth_id; break; } } if (ii == nn_pins) LOG_TEST_RET(ctx, SC_ERROR_OBJECT_NOT_FOUND, "No AUTH object found"); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int iasecc_pkcs15_fix_file_access_rule(struct sc_pkcs15_card *p15card, struct sc_file *file, unsigned ac_op, unsigned rule_mode, struct sc_pkcs15_object *object) { struct sc_context *ctx = p15card->card->ctx; const struct sc_acl_entry *acl = NULL; struct sc_pkcs15_id id; unsigned ref; int rv; LOG_FUNC_CALLED(ctx); acl = sc_file_get_acl_entry(file, ac_op); sc_log(ctx, "Fix file access rule: AC_OP:%i, ACL(method:0x%X,ref:0x%X)", ac_op, acl->method, acl->key_ref); if (acl->method == SC_AC_NONE) { sc_log(ctx, "rule-mode:0x%X, auth-ID:NONE", rule_mode); rv = iasecc_pkcs15_add_access_rule(object, rule_mode, NULL); LOG_TEST_RET(ctx, rv, "Fix file access rule error"); } else { if (acl->method == SC_AC_IDA) { ref = acl->key_ref; iasecc_reference_to_pkcs15_id (ref, &id); } else if (acl->method == SC_AC_SCB) { rv = iasecc_pkcs15_get_auth_id_from_se(p15card, acl->key_ref, &id); LOG_TEST_RET(ctx, rv, "Cannot get AUTH.ID from SE"); } else if (acl->method == SC_AC_PRO) { ref = IASECC_SCB_METHOD_SM * 0x100 + acl->key_ref; iasecc_reference_to_pkcs15_id (ref, &id); } else { LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Fix file access error"); } sc_log(ctx, "rule-mode:0x%X, auth-ID:%s", rule_mode, sc_pkcs15_print_id(&id)); rv = iasecc_pkcs15_add_access_rule(object, rule_mode, &id); LOG_TEST_RET(ctx, rv, "Fix file access rule error"); } LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int iasecc_pkcs15_fix_file_access(struct sc_pkcs15_card *p15card, struct sc_file *file, struct sc_pkcs15_object *object) { struct sc_context *ctx = p15card->card->ctx; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "authID %s", sc_pkcs15_print_id(&object->auth_id)); memset(object->access_rules, 0, sizeof(object->access_rules)); rv = iasecc_pkcs15_fix_file_access_rule(p15card, file, SC_AC_OP_READ, SC_PKCS15_ACCESS_RULE_MODE_READ, object); LOG_TEST_RET(ctx, rv, "Fix file READ access error"); rv = iasecc_pkcs15_fix_file_access_rule(p15card, file, SC_AC_OP_UPDATE, SC_PKCS15_ACCESS_RULE_MODE_UPDATE, object); LOG_TEST_RET(ctx, rv, "Fix file READ access error"); rv = iasecc_pkcs15_fix_file_access_rule(p15card, file, SC_AC_OP_DELETE, SC_PKCS15_ACCESS_RULE_MODE_DELETE, object); LOG_TEST_RET(ctx, rv, "Fix file READ access error"); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } int iasecc_pkcs15_encode_supported_algos(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *object) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_prkey_info *prkey_info = (struct sc_pkcs15_prkey_info *) object->data; struct sc_supported_algo_info *algo; int rv = SC_SUCCESS, ii; LOG_FUNC_CALLED(ctx); sc_log(ctx, "encode supported algos for object(%s,type:%X)", object->label, object->type); switch (object->type) { case SC_PKCS15_TYPE_PRKEY_RSA: sc_log(ctx, "PrKey Usage:%X,Access:%X", prkey_info->usage, prkey_info->access_flags); if (prkey_info->usage & (SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP)) { algo = sc_pkcs15_get_supported_algo(p15card, SC_PKCS15_ALGO_OP_DECIPHER, CKM_RSA_PKCS); rv = sc_pkcs15_add_supported_algo_ref(object, algo); LOG_TEST_RET(ctx, rv, "cannot add supported algorithm DECIPHER:CKM_RSA_PKCS"); } 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); LOG_TEST_RET(ctx, rv, "cannot add supported algorithm SIGN:CKM_SHA1_RSA_PKCS"); algo = sc_pkcs15_get_supported_algo(p15card, SC_PKCS15_ALGO_OP_COMPUTE_SIGNATURE, CKM_SHA256_RSA_PKCS); rv = sc_pkcs15_add_supported_algo_ref(object, algo); LOG_TEST_RET(ctx, rv, "cannot add supported algorithm SIGN:CKM_SHA256_RSA_PKCS"); } else { algo = sc_pkcs15_get_supported_algo(p15card, SC_PKCS15_ALGO_OP_COMPUTE_SIGNATURE, CKM_RSA_PKCS); rv = sc_pkcs15_add_supported_algo_ref(object, algo); LOG_TEST_RET(ctx, rv, "cannot add supported algorithm SIGN:CKM_RSA_PKCS"); } } for (ii=0; iialgo_refs[ii]; ii++) sc_log(ctx, "algoReference %i", prkey_info->algo_refs[ii]); break; default: rv = SC_ERROR_NOT_SUPPORTED; break; } LOG_FUNC_RETURN(ctx, rv); } /* * Store SDO key RSA */ static int iasecc_sdo_store_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct iasecc_sdo *sdo_prvkey, struct iasecc_sdo *sdo_pubkey, struct sc_pkcs15_prkey_rsa *rsa) { struct sc_context *ctx = p15card->card->ctx; struct sc_card *card = p15card->card; unsigned long caps = card->caps; struct iasecc_sdo_rsa_update update; struct sc_file *dummy_file = NULL; int rv; LOG_FUNC_CALLED(ctx); if (!sdo_prvkey && !sdo_pubkey) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "At least one SDO has to be supplied"); rv = iasecc_sdo_convert_to_file(card, sdo_prvkey ? sdo_prvkey : sdo_pubkey, &dummy_file); LOG_TEST_RET(ctx, rv, "Cannot convert SDO PRIVATE KEY to file"); card->caps &= ~SC_CARD_CAP_USE_FCI_AC; rv = sc_pkcs15init_authenticate(profile, p15card, dummy_file, SC_AC_OP_UPDATE); card->caps = caps; sc_file_free(dummy_file); LOG_TEST_RET(ctx, rv, "SDO PRIVATE KEY UPDATE authentication failed"); memset(&update, 0, sizeof(update)); update.sdo_prv_key = sdo_prvkey; update.sdo_pub_key = sdo_pubkey; update.p15_rsa = rsa; update.magic = IASECC_SDO_MAGIC_UPDATE_RSA; rv = sc_card_ctl(card, SC_CARDCTL_IASECC_SDO_KEY_RSA_PUT_DATA, &update); LOG_TEST_RET(ctx, rv, "store IAS SDO PRIVATE KEY failed"); LOG_FUNC_RETURN(ctx, rv); } static int iasecc_pkcs15_add_algorithm_reference(struct sc_pkcs15_card *p15card, struct sc_pkcs15_prkey_info *key_info, unsigned algo_ref) { int ii, jj; for (jj=0;jjalgo_refs[jj];jj++) ; if (jj == SC_MAX_SUPPORTED_ALGORITHMS) return SC_ERROR_TOO_MANY_OBJECTS; for (ii=0;iitokeninfo->supported_algos[ii].algo_ref == algo_ref) break; if (ii == SC_MAX_SUPPORTED_ALGORITHMS) return SC_ERROR_OBJECT_NOT_FOUND; key_info->algo_refs[jj] = p15card->tokeninfo->supported_algos[ii].reference; return SC_SUCCESS; } static int iasecc_pkcs15_fix_private_key_attributes(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *object, struct iasecc_sdo *sdo_prvkey) { struct sc_card *card = p15card->card; struct sc_context *ctx = card->ctx; struct sc_pkcs15_prkey_info *key_info = (struct sc_pkcs15_prkey_info *) object->data; int rv = 0, ii; unsigned keys_access_modes[IASECC_MAX_SCBS] = { SC_PKCS15_ACCESS_RULE_MODE_PSO_CDS, SC_PKCS15_ACCESS_RULE_MODE_INT_AUTH, SC_PKCS15_ACCESS_RULE_MODE_PSO_DECRYPT, SC_PKCS15_ACCESS_RULE_MODE_EXECUTE, 0x00, SC_PKCS15_ACCESS_RULE_MODE_UPDATE, SC_PKCS15_ACCESS_RULE_MODE_READ }; LOG_FUNC_CALLED(ctx); if (!object->content.value || object->content.len != sizeof(struct iasecc_sdo)) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "store IAS SDO PRIVATE KEY failed"); if (object->type != SC_PKCS15_TYPE_PRKEY_RSA) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Unsupported object type"); key_info->access_flags |= SC_PKCS15_PRKEY_ACCESS_SENSITIVE; sc_log(ctx, "SDO(class:%X,ref:%X,usage:%X)", sdo_prvkey->sdo_class, sdo_prvkey->sdo_ref, sdo_prvkey->usage); sc_log(ctx, "SDO ACLs(%"SC_FORMAT_LEN_SIZE_T"u):%s", sdo_prvkey->docp.acls_contact.size, sc_dump_hex(sdo_prvkey->docp.acls_contact.value, sdo_prvkey->docp.acls_contact.size)); sc_log(ctx, "SDO AMB:%X, SCBS:%s", sdo_prvkey->docp.amb, sc_dump_hex(sdo_prvkey->docp.scbs, IASECC_MAX_SCBS)); for (ii=0;iidocp.scbs[ii]); if (sdo_prvkey->docp.scbs[ii] == 0xFF) { continue; } else if (sdo_prvkey->docp.scbs[ii] == 0x00) { rv = iasecc_pkcs15_add_access_rule(object, keys_access_modes[ii], NULL); LOG_TEST_RET(ctx, rv, "Cannot add access rule"); } else if (sdo_prvkey->docp.scbs[ii] & IASECC_SCB_METHOD_USER_AUTH) { struct sc_pkcs15_id auth_id; rv = iasecc_pkcs15_get_auth_id_from_se(p15card, sdo_prvkey->docp.scbs[ii], &auth_id); LOG_TEST_RET(ctx, rv, "Cannot get AUTH.ID from SE"); rv = iasecc_pkcs15_add_access_rule(object, keys_access_modes[ii], &auth_id); LOG_TEST_RET(ctx, rv, "Cannot add access rule"); if (ii == IASECC_ACLS_RSAKEY_PSO_SIGN || ii == IASECC_ACLS_RSAKEY_INTERNAL_AUTH || ii == IASECC_ACLS_RSAKEY_PSO_DECIPHER) { if (!sc_pkcs15_compare_id(&object->auth_id, &auth_id)) { /* Sorry, this will silently overwrite the profile option.*/ sc_log(ctx, "Change object's authId for the one that really protects crypto operation."); object->auth_id = auth_id; } rv = iasecc_pkcs15_add_access_rule(object, SC_PKCS15_ACCESS_RULE_MODE_EXECUTE, &auth_id); LOG_TEST_RET(ctx, rv, "Cannot add 'EXECUTE' access rule"); } } if (ii == IASECC_ACLS_RSAKEY_PSO_SIGN) { rv = iasecc_pkcs15_add_algorithm_reference(p15card, key_info, IASECC_ALGORITHM_RSA_PKCS | IASECC_ALGORITHM_SHA1); LOG_TEST_RET(ctx, rv, "Cannot add RSA_PKCS SHA1 supported mechanism"); rv = iasecc_pkcs15_add_algorithm_reference(p15card, key_info, IASECC_ALGORITHM_RSA_PKCS | IASECC_ALGORITHM_SHA2); LOG_TEST_RET(ctx, rv, "Cannot add RSA_PKCS SHA2 supported mechanism"); if (sdo_prvkey->docp.non_repudiation.value && sdo_prvkey->docp.non_repudiation.value[0]) { object->user_consent = 1; } } else if (ii == IASECC_ACLS_RSAKEY_INTERNAL_AUTH) { rv = iasecc_pkcs15_add_algorithm_reference(p15card, key_info, IASECC_ALGORITHM_RSA_PKCS); LOG_TEST_RET(ctx, rv, "Cannot add RSA_PKCS supported mechanism"); } else if (ii == IASECC_ACLS_RSAKEY_PSO_DECIPHER) { rv = iasecc_pkcs15_add_algorithm_reference(p15card, key_info, IASECC_ALGORITHM_RSA_PKCS_DECRYPT | IASECC_ALGORITHM_SHA1); LOG_TEST_RET(ctx, rv, "Cannot add decipher RSA_PKCS supported mechanism"); } } LOG_FUNC_RETURN(ctx, rv); } static int iasecc_pkcs15_create_key_slot(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct iasecc_sdo *sdo_prvkey, struct iasecc_sdo *sdo_pubkey, struct sc_pkcs15_prkey_info *key_info) { struct sc_context *ctx = p15card->card->ctx; struct sc_card *card = p15card->card; struct sc_file *file_p_pubkey = NULL, *file_p_prvkey = NULL, *parent = NULL; unsigned long save_card_caps = p15card->card->caps; int rv; LOG_FUNC_CALLED(ctx); rv = iasecc_pkcs15_new_file(profile, card, SC_PKCS15_TYPE_PRKEY_RSA, key_info->key_reference, &file_p_prvkey); LOG_TEST_GOTO_ERR(ctx, rv, "create key slot: cannot instantiate PRKEY_RSA file"); rv = iasecc_pkcs15_new_file(profile, card, SC_PKCS15_TYPE_PUBKEY_RSA, key_info->key_reference, &file_p_pubkey); LOG_TEST_GOTO_ERR(ctx, rv, "create key slot: cannot instantiate PUBKEY_RSA file"); rv = iasecc_file_convert_acls(ctx, profile, file_p_prvkey); LOG_TEST_GOTO_ERR(ctx, rv, "create key slot: cannot convert ACLs of the private key file"); rv = iasecc_file_convert_acls(ctx, profile, file_p_pubkey); LOG_TEST_GOTO_ERR(ctx, rv, "create key slot: cannot convert ACLs of the public key file"); rv = sc_profile_get_parent(profile, "private-key", &parent); LOG_TEST_GOTO_ERR(ctx, rv, "create key slot: cannot get parent of private key file"); rv = iasecc_file_convert_acls(ctx, profile, parent); LOG_TEST_GOTO_ERR(ctx, rv, "create key slot: cannot convert parent's ACLs"); /* Oberthur's card do not returns FCP for selected application DF. * That's why for the following authentication use the 'CREATE' ACL defined in the application profile. */ if (card->type == SC_CARD_TYPE_IASECC_OBERTHUR) p15card->card->caps &= ~SC_CARD_CAP_USE_FCI_AC; rv = sc_pkcs15init_authenticate(profile, p15card, parent, SC_AC_OP_CREATE); p15card->card->caps = save_card_caps; LOG_TEST_GOTO_ERR(ctx, rv, "create key slot: SC_AC_OP_CREATE authentication failed"); if (!sdo_prvkey->not_on_card) sc_log(ctx, "create key slot: SDO private key already present"); else rv = sc_card_ctl(card, SC_CARDCTL_IASECC_SDO_CREATE, sdo_prvkey); LOG_TEST_GOTO_ERR(ctx, rv, "create key slot: cannot create private key: ctl failed"); if (!sdo_pubkey->not_on_card) sc_log(ctx, "create key slot: SDO public key already present"); else rv = sc_card_ctl(card, SC_CARDCTL_IASECC_SDO_CREATE, sdo_pubkey); LOG_TEST_GOTO_ERR(ctx, rv, "create key slot: cannot create public key: ctl failed"); err: sc_file_free(file_p_prvkey); sc_file_free(file_p_pubkey); sc_file_free(parent); LOG_FUNC_RETURN(ctx, rv); } static int iasecc_pkcs15_create_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *object) { struct sc_card *card = p15card->card; struct sc_context *ctx = card->ctx; struct sc_pkcs15_prkey_info *key_info = (struct sc_pkcs15_prkey_info *) object->data; struct iasecc_sdo *sdo_prvkey = NULL, *sdo_pubkey = NULL; size_t keybits = key_info->modulus_length; unsigned char zeros[0x200]; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "create private key(keybits:%"SC_FORMAT_LEN_SIZE_T"u,usage:%X,access:%X,ref:%X)", keybits, key_info->usage, key_info->access_flags, key_info->key_reference); if (keybits < 1024 || keybits > 2048 || (keybits % 256)) { sc_log(ctx, "Unsupported key size %"SC_FORMAT_LEN_SIZE_T"u", keybits); return SC_ERROR_INVALID_ARGUMENTS; } memset(zeros, 0, sizeof(zeros)); rv = iasecc_sdo_allocate_pubkey(profile, card, (struct sc_pkcs15_pubkey_info *)key_info, &sdo_pubkey); LOG_TEST_RET(ctx, rv, "IasEcc: allocate SDO public key failed"); 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_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) { sc_log(ctx, "Key ref %i already allocated", key_info->key_reference); } else { rv = iasecc_pkcs15_create_key_slot(profile, p15card, sdo_prvkey, sdo_pubkey, key_info); 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_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_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); } /* * RSA key generation */ static int iasecc_pkcs15_generate_key(struct sc_profile *profile, sc_pkcs15_card_t *p15card, struct sc_pkcs15_object *object, struct sc_pkcs15_pubkey *pubkey) { struct sc_card *card = p15card->card; struct sc_context *ctx = card->ctx; struct sc_pkcs15_prkey_info *key_info = (struct sc_pkcs15_prkey_info *) object->data; size_t keybits = key_info->modulus_length; struct iasecc_sdo *sdo_prvkey = NULL; struct iasecc_sdo *sdo_pubkey = NULL; struct sc_file *file = NULL; unsigned char *tmp = NULL; size_t tmp_len; unsigned long caps; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "generate key(bits:%"SC_FORMAT_LEN_SIZE_T"u,path:%s,AuthID:%s\n", keybits, sc_print_path(&key_info->path), sc_pkcs15_print_id(&object->auth_id)); if (!object->content.value || object->content.len != sizeof(struct iasecc_sdo)) LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Invalid PrKey SDO data"); sdo_prvkey = (struct iasecc_sdo *)object->content.value; if (sdo_prvkey->magic != SC_CARDCTL_IASECC_SDO_MAGIC) LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "'Magic' control failed for SDO PrvKey"); if (keybits < 1024 || keybits > 2048 || (keybits%0x100)) { sc_log(ctx, "Unsupported key size %"SC_FORMAT_LEN_SIZE_T"u\n", keybits); return SC_ERROR_INVALID_ARGUMENTS; } /* 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"); rv = sc_select_file(card, &file->path, NULL); LOG_TEST_RET(ctx, rv, "DF for private objects not defined"); sc_file_free(file); rv = iasecc_sdo_convert_to_file(card, sdo_prvkey, &file); LOG_TEST_RET(ctx, rv, "Cannot convert SDO PRIVKEY to file"); caps = card->caps; card->caps &= ~SC_CARD_CAP_USE_FCI_AC; rv = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_GENERATE); card->caps = caps; LOG_TEST_RET(ctx, rv, "SC_AC_OP_GENERATE authentication failed"); key_info->access_flags |= SC_PKCS15_PRKEY_ACCESS_LOCAL; key_info->access_flags |= SC_PKCS15_PRKEY_ACCESS_ALWAYSSENSITIVE; key_info->access_flags |= SC_PKCS15_PRKEY_ACCESS_NEVEREXTRACTABLE; rv = sc_card_ctl(card, SC_CARDCTL_IASECC_SDO_GENERATE, sdo_prvkey); LOG_TEST_RET(ctx, rv, "generate key failed"); /* Quite dangerous -- cast of 'sc_pkcs15_prvkey_info' into 'sc_pkcs15_pubkey_info'. */ rv = iasecc_sdo_allocate_pubkey(profile, card, (struct sc_pkcs15_pubkey_info *)key_info, &sdo_pubkey); LOG_TEST_RET(ctx, rv, "IasEcc: allocate SDO public key failed"); pubkey->algorithm = SC_ALGORITHM_RSA; pubkey->u.rsa.modulus.len = sdo_pubkey->data.pub_key.n.size; pubkey->u.rsa.modulus.data = (unsigned char *) malloc(pubkey->u.rsa.modulus.len); if (!pubkey->u.rsa.modulus.data) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); memcpy(pubkey->u.rsa.modulus.data, sdo_pubkey->data.pub_key.n.value, pubkey->u.rsa.modulus.len); pubkey->u.rsa.exponent.len = sdo_pubkey->data.pub_key.e.size; pubkey->u.rsa.exponent.data = (unsigned char *) malloc(pubkey->u.rsa.exponent.len); if (!pubkey->u.rsa.exponent.data) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); memcpy(pubkey->u.rsa.exponent.data, sdo_pubkey->data.pub_key.e.value, pubkey->u.rsa.exponent.len); rv = sc_pkcs15_encode_pubkey(ctx, pubkey, &tmp, &tmp_len); LOG_TEST_RET(ctx, rv, "encode public key failed"); rv = iasecc_pkcs15_encode_supported_algos(p15card, object); LOG_TEST_RET(ctx, rv, "encode private key access rules failed"); /* SDO PrvKey data replaced by public part of generated key */ rv = sc_pkcs15_allocate_object_content(ctx, object, tmp, tmp_len); LOG_TEST_RET(ctx, rv, "Failed to allocate public key as object content"); iasecc_sdo_free(card, sdo_pubkey); free(tmp); LOG_FUNC_RETURN(ctx, rv); } /* * Store a private key */ static int iasecc_pkcs15_store_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *object, struct sc_pkcs15_prkey *prvkey) { struct sc_card *card = p15card->card; struct sc_context *ctx = card->ctx; struct sc_pkcs15_prkey_info *key_info = (struct sc_pkcs15_prkey_info *) object->data; size_t keybits = key_info->modulus_length; struct iasecc_sdo *sdo_prvkey; struct iasecc_sdo *sdo_pubkey = NULL; struct sc_pkcs15_prkey_rsa *rsa = &prvkey->u.rsa; struct sc_file *file = NULL; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "Store IAS/ECC key(keybits:%"SC_FORMAT_LEN_SIZE_T"u,AuthID:%s,path:%s)", keybits, sc_pkcs15_print_id(&object->auth_id), sc_print_path(&key_info->path)); if (!object->content.value || object->content.len != sizeof(struct iasecc_sdo)) LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Invalid PrKey SDO data"); else if (keybits < 1024 || keybits > 2048 || (keybits%0x100)) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Unsupported key size"); sdo_prvkey = (struct iasecc_sdo *)object->content.value; if (sdo_prvkey->magic != SC_CARDCTL_IASECC_SDO_MAGIC) LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "'Magic' control failed for SDO PrvKey"); sc_log(ctx, "key compulsory attr(size:%"SC_FORMAT_LEN_SIZE_T"u,on_card:%i)", sdo_prvkey->data.prv_key.compulsory.size, sdo_prvkey->data.prv_key.compulsory.on_card); rv = sc_profile_get_parent(profile, "private-key", &file); LOG_TEST_RET(ctx, rv, "cannot instantiate parent DF of the private key"); rv = sc_select_file(card, &file->path, NULL); LOG_TEST_RET(ctx, rv, "failed to select parent DF"); sc_file_free(file); key_info->access_flags &= ~SC_PKCS15_PRKEY_ACCESS_LOCAL; rv = iasecc_sdo_allocate_pubkey(profile, card, (struct sc_pkcs15_pubkey_info *)key_info, &sdo_pubkey); LOG_TEST_RET(ctx, rv, "private key store failed: cannot allocate 'SDO PUBLIC KEY'"); rv = iasecc_sdo_store_key(profile, p15card, sdo_prvkey, sdo_pubkey, rsa); LOG_TEST_RET(ctx, rv, "cannot store SDO PRIVATE/PUBLIC KEYs"); /* sdo_prvkey is freed while object is freeing */ iasecc_sdo_free(card, sdo_pubkey); LOG_FUNC_RETURN(ctx, rv); } static int iasecc_pkcs15_delete_sdo (struct sc_profile *profile, struct sc_pkcs15_card *p15card, int sdo_class, int ref) { struct sc_context *ctx = p15card->card->ctx; struct sc_card *card = p15card->card; struct iasecc_sdo *sdo = NULL; struct sc_pkcs15_prkey_rsa rsa; struct sc_file *dummy_file = NULL; unsigned long save_card_caps = card->caps; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "iasecc_pkcs15_delete_sdo() class 0x%X; reference %i", sdo_class, ref); sdo = calloc(1, sizeof(struct iasecc_sdo)); if (!sdo) return SC_ERROR_OUT_OF_MEMORY; sdo->magic = SC_CARDCTL_IASECC_SDO_MAGIC; sdo->sdo_class = sdo_class; sdo->sdo_ref = ref & 0x3F; rv = iasecc_sdo_get_data(card, sdo); if (rv < 0) { if (rv == SC_ERROR_DATA_OBJECT_NOT_FOUND) rv = SC_SUCCESS; iasecc_sdo_free(card, sdo); LOG_FUNC_RETURN(ctx, rv); } if (sdo->sdo_class == IASECC_SDO_CLASS_RSA_PUBLIC) { if (sdo->data.pub_key.cha.value) { free(sdo->data.pub_key.cha.value); sdo->data.pub_key.cha.value = NULL; sdo->data.pub_key.cha.size = 0; } } sc_log(ctx, "iasecc_pkcs15_delete_sdo() SDO class 0x%X, ref 0x%X", sdo->sdo_class, sdo->sdo_ref); rv = iasecc_sdo_convert_to_file(card, sdo, &dummy_file); if (rv < 0) { iasecc_sdo_free(card, sdo); LOG_TEST_RET(ctx, rv, "iasecc_pkcs15_delete_sdo() Cannot convert SDO to file"); } card->caps &= ~SC_CARD_CAP_USE_FCI_AC; rv = sc_pkcs15init_authenticate(profile, p15card, dummy_file, SC_AC_OP_UPDATE); card->caps = save_card_caps; sc_file_free(dummy_file); if (rv < 0) { iasecc_sdo_free(card, sdo); LOG_TEST_RET(ctx, rv, "iasecc_pkcs15_delete_sdo() UPDATE authentication failed for SDO"); } if (card->type == SC_CARD_TYPE_IASECC_OBERTHUR) { /* Oberthur's card supports creation/deletion of the key slots ... */ rv = sc_card_ctl(card, SC_CARDCTL_IASECC_SDO_DELETE, sdo); } else { /* ... other cards not. * Set to zero the key components . */ unsigned char zeros[0x200]; int size = *(sdo->docp.size.value + 0) * 0x100 + *(sdo->docp.size.value + 1); sc_log(ctx, "iasecc_pkcs15_delete_sdo() SDO size %i bytes", size); memset(zeros, 0xA5, sizeof(zeros)); memset(&rsa, 0, sizeof(rsa)); rsa.modulus.data = rsa.exponent.data = zeros; rsa.modulus.len = size; rsa.exponent.len = 3; rsa.p.data = rsa.q.data = rsa.iqmp.data = rsa.dmp1.data = rsa.dmq1.data = zeros; rsa.p.len = rsa.q.len = rsa.iqmp.len = rsa.dmp1.len = rsa.dmq1.len = size/2; /* Don't know why, but, clean public key do not working with Gemalto card */ rv = iasecc_sdo_store_key(profile, p15card, sdo, NULL, &rsa); } iasecc_sdo_free(card, sdo); LOG_FUNC_RETURN(ctx, rv); } static int iasecc_pkcs15_delete_object (struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *object, const struct sc_path *path) { struct sc_context *ctx = p15card->card->ctx; struct sc_file *file = NULL; int rv, key_ref; LOG_FUNC_CALLED(ctx); switch(object->type & SC_PKCS15_TYPE_CLASS_MASK) { case SC_PKCS15_TYPE_PUBKEY: key_ref = ((struct sc_pkcs15_pubkey_info *)object->data)->key_reference; sc_log(ctx, "Ignore delete of SDO-PubKey(ref:%X) '%s', path %s", key_ref, object->label, sc_print_path(path)); LOG_FUNC_RETURN(ctx, SC_SUCCESS); case SC_PKCS15_TYPE_PRKEY: sc_log(ctx, "delete PrivKey '%s', path %s", object->label, sc_print_path(path)); if (path->len || path->aid.len) { rv = sc_select_file(p15card->card, path, NULL); LOG_TEST_RET(ctx, rv, "cannot select PrivKey path"); } key_ref = ((struct sc_pkcs15_prkey_info *)object->data)->key_reference; /* Delete both parts of the RSA key */ rv = iasecc_pkcs15_delete_sdo (profile, p15card, IASECC_SDO_CLASS_RSA_PRIVATE, key_ref); LOG_TEST_RET(ctx, rv, "Cannot delete RSA_PRIVATE SDO"); rv = iasecc_pkcs15_delete_sdo (profile, p15card, IASECC_SDO_CLASS_RSA_PUBLIC, key_ref); LOG_TEST_RET(ctx, rv, "Cannot delete RSA_PUBLIC SDO"); if (profile->md_style == SC_PKCS15INIT_MD_STYLE_GEMALTO) { rv = iasecc_md_gemalto_delete_prvkey(p15card, profile, object); LOG_TEST_RET(ctx, rv, "MD error: cannot delete private key"); } LOG_FUNC_RETURN(ctx, rv); case SC_PKCS15_TYPE_CERT: sc_log(ctx, "delete Certificate '%s', path %s", object->label, sc_print_path(path)); break; case SC_PKCS15_TYPE_DATA_OBJECT: sc_log(ctx, "delete DataObject '%s', path %s", object->label, sc_print_path(path)); break; default: LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); } file = sc_file_new(); file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = SC_FILE_EF_TRANSPARENT; file->id = path->value[path->len-2] * 0x100 + path->value[path->len-1]; memcpy(&file->path, path, sizeof(file->path)); rv = iasecc_pkcs15_delete_file(p15card, profile, file); sc_file_free(file); LOG_FUNC_RETURN(ctx, rv); } static int iasecc_md_gemalto_set_default(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15_object *key_obj) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_object *data_obj = NULL; struct sc_pkcs15init_dataargs data_args; unsigned char guid[40]; size_t guid_len; int rv; LOG_FUNC_CALLED(ctx); rv = sc_pkcs15_find_data_object_by_name(p15card, "CSP", "Default Key Container", &data_obj); if (rv != SC_ERROR_OBJECT_NOT_FOUND) LOG_TEST_RET(ctx, rv, "Find 'Default Key Container' data object error"); memset(guid, 0, sizeof(guid)); guid_len = sizeof(guid); rv = sc_pkcs15_get_object_guid(p15card, key_obj, 1, guid, &guid_len); LOG_TEST_RET(ctx, rv, "Cannot get private key GUID"); if (!data_obj) { memset(&data_args, 0, sizeof(data_args)); sc_init_oid(&data_args.app_oid); data_args.label = "Default Key Container"; data_args.app_label = "CSP"; data_args.der_encoded.value = guid; data_args.der_encoded.len = guid_len; rv = sc_pkcs15init_store_data_object(p15card, profile, &data_args, NULL); LOG_TEST_RET(ctx, rv, "Failed to store 'CSP'/'Default Key Container' data object"); } else { struct sc_pkcs15_data_info *dinfo = (struct sc_pkcs15_data_info *)data_obj->data; struct sc_file *file = NULL; sc_log(ctx, "update data object content in '%s'\n", sc_print_path(&dinfo->path)); rv = sc_select_file(p15card->card, &dinfo->path, &file); LOG_TEST_RET(ctx, rv, "Cannot select data object file"); rv = sc_pkcs15init_update_file(profile, p15card, file, guid, guid_len); sc_file_free(file); LOG_TEST_RET(ctx, rv, "Failed to update 'CSP'/'Default Key Container' data object"); } LOG_FUNC_RETURN(ctx, rv); } static int iasecc_md_gemalto_unset_default(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15_object *key_obj) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_object *data_obj = NULL; struct sc_pkcs15_data *dod = NULL; struct sc_pkcs15_object *key_objs[32]; 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, private_obj; LOG_FUNC_CALLED(ctx); memset(guid, 0, sizeof(guid)); guid_len = sizeof(guid); rv = sc_pkcs15_get_object_guid(p15card, key_obj, 1, guid, &guid_len); LOG_TEST_RET(ctx, rv, "Cannot get private key GUID"); rv = sc_pkcs15_find_data_object_by_name(p15card, "CSP", "Default Key Container", &data_obj); if (rv == SC_ERROR_OBJECT_NOT_FOUND) LOG_FUNC_RETURN(ctx, SC_SUCCESS); 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)) { 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"); keys_num = rv; if (keys_num) { for (ii=0; iidata; if (sc_pkcs15_compare_id(&key_info->id, &prkey_info->id)) continue; /* TODO: keys with inappropriate key usages should also be ignored */ rv = iasecc_md_gemalto_set_default(p15card, profile, key_objs[ii]); LOG_TEST_RET(ctx, rv, "Cannot set default container"); break; } if (ii == keys_num) { /* No more default container */ rv = sc_pkcs15init_delete_object(p15card, profile, data_obj); LOG_TEST_RET(ctx, rv, "Cannot delete 'CSP'/'Default Key Container' data object"); } } LOG_FUNC_RETURN(ctx, rv); } static int iasecc_md_gemalto_new_prvkey(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15_object *key_obj) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_prkey_info *prkey_info = (struct sc_pkcs15_prkey_info *)key_obj->data; struct sc_pkcs15init_dataargs data_args; unsigned char data[SC_PKCS15_MAX_ID_SIZE + 6]; unsigned char guid[40]; size_t offs, guid_len; int rv; LOG_FUNC_CALLED(ctx); memset(guid, 0, sizeof(guid)); guid_len = sizeof(guid) - 1; rv = sc_pkcs15_get_object_guid(p15card, key_obj, 1, guid, &guid_len); LOG_TEST_RET(ctx, rv, "Cannot get private key GUID"); sc_log(ctx, "New key GUID: '%s'", (char *)guid); offs = 0; data[offs++] = 0x01; data[offs++] = prkey_info->id.len; memcpy(&data[offs], prkey_info->id.value, prkey_info->id.len); offs += prkey_info->id.len; data[offs++] = 0x02; data[offs++] = 0x01; data[offs++] = 0x01; memset(&data_args, 0, sizeof(data_args)); sc_init_oid(&data_args.app_oid); data_args.label = (char *)guid; data_args.app_label = "CSP"; data_args.der_encoded.value = data; data_args.der_encoded.len = offs; rv = sc_pkcs15init_store_data_object(p15card, profile, &data_args, NULL); LOG_TEST_RET(ctx, rv, "Failed to store 'CSP' data object"); /* For a while default container is set for the first key. * TODO: Key usage should be taken into consideration. */ if (sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_PRKEY, NULL, 0) == 1) { rv = iasecc_md_gemalto_set_default(p15card, profile, key_obj); LOG_TEST_RET(ctx, rv, "MD: cannot set default container"); } LOG_FUNC_RETURN(ctx, rv); } static int iasecc_md_gemalto_delete_prvkey(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15_object *key_obj) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_object *data_obj = NULL; unsigned char guid[40]; size_t guid_len; int rv; LOG_FUNC_CALLED(ctx); memset(guid, 0, sizeof(guid)); guid_len = sizeof(guid) - 1; rv = sc_pkcs15_get_object_guid(p15card, key_obj, 1, guid, &guid_len); LOG_TEST_RET(ctx, rv, "Cannot get private key GUID"); rv = sc_pkcs15_find_data_object_by_name(p15card, "CSP", (char *)guid, &data_obj); if (rv == SC_ERROR_OBJECT_NOT_FOUND) LOG_FUNC_RETURN(ctx, SC_SUCCESS); LOG_TEST_RET(ctx, rv, "Find 'CSP'/ data object error"); rv = sc_pkcs15init_delete_object(p15card, profile, data_obj); LOG_TEST_RET(ctx, rv, "Cannot delete 'CSP'/ data object"); /* For a while default container is set for the first key. * TODO: Key usage should be taken into consideration. */ if (sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_PRKEY, NULL, 0) == 1) { rv = iasecc_md_gemalto_unset_default(p15card, profile, key_obj); LOG_TEST_RET(ctx, rv, "MD: cannot set default container"); } LOG_FUNC_RETURN(ctx, rv); } static int iasecc_store_prvkey(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15_object *object, struct sc_pkcs15_der *data, struct sc_path *path) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_prkey_info *prkey_info = (struct sc_pkcs15_prkey_info *)object->data; LOG_FUNC_CALLED(ctx); sc_log(ctx, "Private Key id '%s'", sc_pkcs15_print_id(&prkey_info->id)); sc_log(ctx, "MD style '0x%X'", profile->md_style); if (profile->md_style == SC_PKCS15INIT_MD_STYLE_NONE) { LOG_FUNC_RETURN(ctx, SC_SUCCESS); } else if (profile->md_style == SC_PKCS15INIT_MD_STYLE_GEMALTO) { int rv = iasecc_md_gemalto_new_prvkey(p15card, profile, object); LOG_TEST_RET(ctx, rv, "MD: cannot add new key"); } else { LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Unsupported MD style"); } LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int iasecc_store_pubkey(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15_object *object, struct sc_pkcs15_der *data, struct sc_path *path) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_pubkey_info *pubkey_info = (struct sc_pkcs15_pubkey_info *)object->data; struct sc_pkcs15_prkey_info *prkey_info = NULL; struct sc_pkcs15_object *prkey_object = NULL; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "Public Key id '%s'", sc_pkcs15_print_id(&pubkey_info->id)); rv = sc_pkcs15_find_prkey_by_id(p15card, &pubkey_info->id, &prkey_object); LOG_TEST_RET(ctx, rv, "Find related PrKey error"); prkey_info = (struct sc_pkcs15_prkey_info *)prkey_object->data; pubkey_info->key_reference = prkey_info->key_reference; pubkey_info->access_flags = prkey_info->access_flags & SC_PKCS15_PRKEY_ACCESS_LOCAL; pubkey_info->access_flags |= SC_PKCS15_PRKEY_ACCESS_EXTRACTABLE; pubkey_info->native = 0; pubkey_info->usage |= prkey_info->usage & SC_PKCS15_PRKEY_USAGE_SIGN ? SC_PKCS15_PRKEY_USAGE_VERIFY : 0; pubkey_info->usage |= prkey_info->usage & SC_PKCS15_PRKEY_USAGE_SIGNRECOVER ? SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER : 0; pubkey_info->usage |= prkey_info->usage & SC_PKCS15_PRKEY_USAGE_NONREPUDIATION ? SC_PKCS15_PRKEY_USAGE_VERIFY : 0; 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; 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)); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int iasecc_store_cert(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15_object *object, struct sc_pkcs15_der *data, struct sc_path *path) { struct sc_context *ctx = p15card->card->ctx; struct sc_card *card = p15card->card; struct sc_file *pfile = NULL; struct sc_path parent_path; int rv; LOG_FUNC_CALLED(ctx); sc_log(ctx, "iasecc_store_cert() authID '%s'", sc_pkcs15_print_id(&object->auth_id)); rv = iasecc_pkcs15_new_file(profile, card, SC_PKCS15_TYPE_CERT, 0, &pfile); LOG_TEST_RET(ctx, rv, "IasEcc new CERT file error"); parent_path = pfile->path; if (parent_path.len >= 2) parent_path.len -= 2; if (!parent_path.len && !parent_path.aid.len) sc_format_path("3F00", &parent_path); rv = sc_select_file(card, &parent_path, NULL); LOG_TEST_RET(ctx, rv, "cannot select parent of certificate to store"); rv = iasecc_pkcs15_fix_file_access(p15card, pfile, object); LOG_TEST_RET(ctx, rv, "encode file access rules failed"); sc_file_free(pfile); /* NOT_IMPLEMENTED error code indicates to the upper call to execute the default 'store data' procedure */ LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_IMPLEMENTED); } static int iasecc_store_data_object(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15_object *object, struct sc_pkcs15_der *data, struct sc_path *path) { #define MAX_DATA_OBJS 32 struct sc_context *ctx = p15card->card->ctx; 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; 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); LOG_TEST_RET(ctx, rv, "iasecc_store_data_object() pkcs15 new DATA file error"); for (ii=0; iidata; int file_id = info->path.value[info->path.len - 2] * 0x100 + info->path.value[info->path.len - 1]; sc_log(ctx, "iasecc_store_data_object() %i: file_id 0x%X, pfile->id 0x%X\n", ii, file_id, file->id); if (file->id == file_id) break; } if (ii == nn_objs) break; sc_file_free(file); file = NULL; } if (indx == MAX_DATA_OBJS) LOG_TEST_GOTO_ERR(ctx, SC_ERROR_TOO_MANY_OBJECTS, "iasecc_store_data_object() too many DATA objects."); do { const struct sc_acl_entry *acl; memset(object->access_rules, 0, sizeof(object->access_rules)); object->access_rules[0].access_mode = SC_PKCS15_ACCESS_RULE_MODE_READ; acl = sc_file_get_acl_entry(file, SC_AC_OP_READ); sc_log(ctx, "iasecc_store_data_object() READ method %i", acl->method); if (acl->method == SC_AC_IDA) iasecc_reference_to_pkcs15_id (acl->key_ref, &object->access_rules[0].auth_id); object->access_rules[1].access_mode = SC_PKCS15_ACCESS_RULE_MODE_UPDATE; acl = sc_file_get_acl_entry(file, SC_AC_OP_UPDATE); sc_log(ctx, "iasecc_store_data_object() UPDATE method %i", acl->method); if (acl->method == SC_AC_IDA) iasecc_reference_to_pkcs15_id (acl->key_ref, &object->access_rules[1].auth_id); object->access_rules[2].access_mode = SC_PKCS15_ACCESS_RULE_MODE_DELETE; acl = sc_file_get_acl_entry(file, SC_AC_OP_DELETE); sc_log(ctx, "iasecc_store_data_object() UPDATE method %i", acl->method); if (acl->method == SC_AC_IDA) iasecc_reference_to_pkcs15_id (acl->key_ref, &object->access_rules[2].auth_id); } while(0); rv = iasecc_file_convert_acls(ctx, profile, file); LOG_TEST_GOTO_ERR(ctx, rv, "iasecc_store_data_object() cannot convert profile ACLs"); rv = sc_profile_get_parent(profile, "public-data", &parent); LOG_TEST_GOTO_ERR(ctx, rv, "iasecc_store_data_object() cannot get object parent"); sc_log(ctx, "iasecc_store_data_object() parent path '%s'\n", sc_print_path(&parent->path)); rv = sc_select_file(card, &parent->path, NULL); 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_GOTO_ERR(ctx, rv, "iasecc_store_data_object() DELETE authentication failed"); rv = iasecc_pkcs15_delete_file(p15card, profile, cfile); 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_GOTO_ERR(ctx, rv, "iasecc_store_data_object() select file error"); } rv = sc_pkcs15init_authenticate(profile, p15card, parent, SC_AC_OP_CREATE); 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_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_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_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); LOG_FUNC_RETURN(ctx, rv); #undef MAX_DATA_OBJS } static int iasecc_emu_store_data(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15_object *object, struct sc_pkcs15_der *data, struct sc_path *path) { struct sc_context *ctx = p15card->card->ctx; int rv = SC_ERROR_NOT_IMPLEMENTED; LOG_FUNC_CALLED(ctx); switch (object->type & SC_PKCS15_TYPE_CLASS_MASK) { case SC_PKCS15_TYPE_PRKEY: rv = iasecc_store_prvkey(p15card, profile, object, data, path); break; case SC_PKCS15_TYPE_PUBKEY: rv = iasecc_store_pubkey(p15card, profile, object, data, path); break; case SC_PKCS15_TYPE_CERT: rv = iasecc_store_cert(p15card, profile, object, data, path); break; case SC_PKCS15_TYPE_DATA_OBJECT: rv = iasecc_store_data_object(p15card, profile, object, data, path); break; default: rv = SC_ERROR_NOT_IMPLEMENTED; break; } LOG_FUNC_RETURN(ctx, rv); } static struct sc_pkcs15init_operations sc_pkcs15init_iasecc_operations = { iasecc_pkcs15_erase_card, NULL, /* init_card */ NULL, /* create_dir */ NULL, /* create_domain */ NULL, /* select_pin_reference */ NULL, /* create_pin */ iasecc_pkcs15_select_key_reference, iasecc_pkcs15_create_key, iasecc_pkcs15_store_key, iasecc_pkcs15_generate_key, NULL, /* encode private key */ NULL, /* encode public key */ NULL, /* finalize_card */ iasecc_pkcs15_delete_object, NULL, /* pkcs15init emulation update_dir */ NULL, /* pkcs15init emulation update_any_df */ NULL, /* pkcs15init emulation update_tokeninfo */ NULL, /* pkcs15init emulation write_info */ iasecc_emu_store_data, NULL, /* sanity_check */ }; struct sc_pkcs15init_operations * 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 */ OpenSC-0.26.1/src/pkcs15init/pkcs15-iasecc.h000066400000000000000000000020461474147347300202340ustar00rootroot00000000000000/* * 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*/ OpenSC-0.26.1/src/pkcs15init/pkcs15-init.h000066400000000000000000000365731474147347300177640ustar00rootroot00000000000000/* * Function prototypes for pkcs15-init * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PKCS15_INIT_H #define PKCS15_INIT_H #ifdef __cplusplus extern "C" { #endif #include "libopensc/pkcs15.h" #define DEFAULT_PRIVATE_KEY_LABEL "Private Key" #define DEFAULT_SECRET_KEY_LABEL "Secret Key" #define SC_PKCS15INIT_X509_DIGITAL_SIGNATURE 0x0080UL #define SC_PKCS15INIT_X509_NON_REPUDIATION 0x0040UL #define SC_PKCS15INIT_X509_KEY_ENCIPHERMENT 0x0020UL #define SC_PKCS15INIT_X509_DATA_ENCIPHERMENT 0x0010UL #define SC_PKCS15INIT_X509_KEY_AGREEMENT 0x0008UL #define SC_PKCS15INIT_X509_KEY_CERT_SIGN 0x0004UL #define SC_PKCS15INIT_X509_CRL_SIGN 0x0002UL typedef struct sc_profile sc_profile_t; /* opaque type */ struct sc_pkcs15init_operations { /* * Erase everything that's on the card */ int (*erase_card)(struct sc_profile *, struct sc_pkcs15_card *); /* * New style API */ /* * Card-specific initialization of PKCS15 meta-information. * Currently used by the cflex driver to read the card's * serial number and use it as the pkcs15 serial number. */ int (*init_card)(struct sc_profile *, struct sc_pkcs15_card *); /* * Create a DF */ int (*create_dir)(struct sc_profile *, struct sc_pkcs15_card *, struct sc_file *); /* * Create a "pin domain". This is for cards such as * the cryptoflex that need to put their pins into * separate directories */ int (*create_domain)(struct sc_profile *, struct sc_pkcs15_card *, const struct sc_pkcs15_id *, struct sc_file **); /* * Select a PIN reference */ int (*select_pin_reference)(struct sc_profile *, struct sc_pkcs15_card *, struct sc_pkcs15_auth_info *); /* * Create a PIN object within the given DF. * * The pin_info object is completely filled in by the caller. * The card driver can reject the pin reference; in this case * the caller needs to adjust it. */ int (*create_pin)(struct sc_profile *, struct sc_pkcs15_card *, struct sc_file *, struct sc_pkcs15_object *, const unsigned char *, size_t, const unsigned char *, size_t); /* * Select a reference for a private key object */ int (*select_key_reference)(struct sc_profile *, struct sc_pkcs15_card *, struct sc_pkcs15_prkey_info *); /* * Create an empty key object. * @index is the number key objects already on the card. * @pin_info contains information on the PIN protecting * the key. NULL if the key should be * unprotected. * @key_info should be filled in by the function */ int (*create_key)(struct sc_profile *, struct sc_pkcs15_card *, struct sc_pkcs15_object *); /* * Store a key on the card */ int (*store_key)(struct sc_profile *, struct sc_pkcs15_card *, struct sc_pkcs15_object *, struct sc_pkcs15_prkey *); /* * Generate key */ int (*generate_key)(struct sc_profile *, struct sc_pkcs15_card *, struct sc_pkcs15_object *, struct sc_pkcs15_pubkey *); /* * Encode private/public key * These are used mostly by the Cryptoflex/Cyberflex drivers. */ int (*encode_private_key)(struct sc_profile *, struct sc_card *, struct sc_pkcs15_prkey_rsa *, unsigned char *, size_t *, int); int (*encode_public_key)(struct sc_profile *, struct sc_card *, struct sc_pkcs15_prkey_rsa *, unsigned char *, size_t *, int); /* * Finalize card * Ends the initialization phase of the smart card/token * (actually this command is currently only for starcos spk 2.3 * cards). */ int (*finalize_card)(struct sc_card *); /* * Delete object */ int (*delete_object)(struct sc_profile *, struct sc_pkcs15_card *, struct sc_pkcs15_object *, const struct sc_path *); /* * Support of pkcs15init emulation */ int (*emu_update_dir) (struct sc_profile *, struct sc_pkcs15_card *, struct sc_app_info *); int (*emu_update_any_df) (struct sc_profile *, struct sc_pkcs15_card *, unsigned, struct sc_pkcs15_object *); int (*emu_update_tokeninfo) (struct sc_profile *, struct sc_pkcs15_card *, struct sc_pkcs15_tokeninfo *); int (*emu_write_info)(struct sc_profile *, struct sc_pkcs15_card *, struct sc_pkcs15_object *); int (*emu_store_data)(struct sc_pkcs15_card *, struct sc_profile *, struct sc_pkcs15_object *, struct sc_pkcs15_der *, struct sc_path *); int (*sanity_check)(struct sc_profile *, struct sc_pkcs15_card *); }; /* Do not change these or reorder these */ #define SC_PKCS15INIT_ID_STYLE_NATIVE 0 #define SC_PKCS15INIT_ID_STYLE_MOZILLA 1 #define SC_PKCS15INIT_ID_STYLE_RFC2459 2 #define SC_PKCS15INIT_SO_PIN 0 #define SC_PKCS15INIT_SO_PUK 1 #define SC_PKCS15INIT_USER_PIN 2 #define SC_PKCS15INIT_USER_PUK 3 #define SC_PKCS15INIT_NPINS 4 #define SC_PKCS15INIT_MD_STYLE_NONE 0 #define SC_PKCS15INIT_MD_STYLE_GEMALTO 1 struct sc_pkcs15init_callbacks { /* * Get a PIN from the front-end. The first argument is * one of the SC_PKCS15INIT_XXX_PIN/PUK macros. */ int (*get_pin)(struct sc_profile *, int, const struct sc_pkcs15_auth_info *, const char *, unsigned char *, size_t *); /* * Get a transport/secure messaging key from the front-end. */ int (*get_key)(struct sc_profile *, int, int, const unsigned char *, size_t, unsigned char *, size_t *); }; struct sc_pkcs15init_initargs { const unsigned char * so_pin; size_t so_pin_len; const unsigned char * so_puk; size_t so_puk_len; const char * so_pin_label; const char * label; const char * serial; }; struct sc_pkcs15init_pinargs { struct sc_pkcs15_id auth_id; const char * label; const unsigned char * pin; size_t pin_len; struct sc_pkcs15_id puk_id; const char * puk_label; const unsigned char * puk; size_t puk_len; }; struct sc_pkcs15init_keyarg_gost_params { unsigned char gostr3410, gostr3411, gost28147; }; struct sc_pkcs15init_prkeyargs { /* TODO: member for private key algorithm: currently is used algorithm from 'key' member */ struct sc_pkcs15_id id; struct sc_pkcs15_id auth_id; char *label; unsigned char *guid; size_t guid_len; 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; } params; struct sc_pkcs15_prkey key; }; struct sc_pkcs15init_keygen_args { struct sc_pkcs15init_prkeyargs prkey_args; const char * pubkey_label; }; struct sc_pkcs15init_pubkeyargs { struct sc_pkcs15_id id; struct sc_pkcs15_id auth_id; const char * label; unsigned int usage; unsigned long x509_usage; union { struct sc_pkcs15init_keyarg_gost_params gost; } params; struct sc_pkcs15_pubkey key; }; struct sc_pkcs15init_dataargs { struct sc_pkcs15_id id; const char * label; struct sc_pkcs15_id auth_id; const char * app_label; struct sc_object_id app_oid; struct sc_pkcs15_der der_encoded; /* Wrong name: is not DER encoded */ }; struct sc_pkcs15init_skeyargs { struct sc_pkcs15_id id; struct sc_pkcs15_id auth_id; const char * label; 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; }; struct sc_pkcs15init_certargs { struct sc_pkcs15_id id; const char * label; int update; unsigned long x509_usage; unsigned char authority; struct sc_pkcs15_der der_encoded; }; #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 *, struct sc_pkcs15_id *, void *); extern void sc_pkcs15init_free_object(struct sc_pkcs15_object *); extern void sc_pkcs15init_set_callbacks(struct sc_pkcs15init_callbacks *); extern int sc_pkcs15init_bind(struct sc_card *, const char *, const char *, struct sc_app_info *app_info, struct sc_profile **); extern void sc_pkcs15init_unbind(struct sc_profile *); extern void sc_pkcs15init_set_p15card(struct sc_profile *, struct sc_pkcs15_card *); extern int sc_pkcs15init_set_lifecycle(struct sc_card *, int); extern int sc_pkcs15init_erase_card(struct sc_pkcs15_card *, struct sc_profile *, struct sc_aid *); /* XXX could this function be merged with ..._set_lifecycle ?? */ extern int sc_pkcs15init_finalize_card(struct sc_card *, struct sc_profile *); extern int sc_pkcs15init_add_app(struct sc_card *, struct sc_profile *, struct sc_pkcs15init_initargs *); extern int sc_pkcs15init_store_pin(struct sc_pkcs15_card *, struct sc_profile *, struct sc_pkcs15init_pinargs *); extern int sc_pkcs15init_generate_key(struct sc_pkcs15_card *, struct sc_profile *, struct sc_pkcs15init_keygen_args *, unsigned int keybits, struct sc_pkcs15_object **); extern int sc_pkcs15init_generate_secret_key(struct sc_pkcs15_card *, struct sc_profile *, struct sc_pkcs15init_skeyargs *, struct sc_pkcs15_object **); extern int sc_pkcs15init_store_private_key(struct sc_pkcs15_card *, struct sc_profile *, struct sc_pkcs15init_prkeyargs *, struct sc_pkcs15_object **); extern int sc_pkcs15init_store_split_key(struct sc_pkcs15_card *, struct sc_profile *, struct sc_pkcs15init_prkeyargs *, struct sc_pkcs15_object **, struct sc_pkcs15_object **); extern int sc_pkcs15init_store_public_key(struct sc_pkcs15_card *, struct sc_profile *, struct sc_pkcs15init_pubkeyargs *, struct sc_pkcs15_object **); extern int sc_pkcs15init_store_secret_key(struct sc_pkcs15_card *, struct sc_profile *, struct sc_pkcs15init_skeyargs *, struct sc_pkcs15_object **); extern int sc_pkcs15init_store_certificate(struct sc_pkcs15_card *, struct sc_profile *, struct sc_pkcs15init_certargs *, struct sc_pkcs15_object **); extern int sc_pkcs15init_store_data_object(struct sc_pkcs15_card *, struct sc_profile *, struct sc_pkcs15init_dataargs *, struct sc_pkcs15_object **); /* Change the value of a pkcs15 attribute. * new_attrib_type can (currently) be either P15_ATTR_TYPE_LABEL or * P15_ATTR_TYPE_ID. * If P15_ATTR_TYPE_LABEL, then *new_value is a struct sc_pkcs15_id; * If P15_ATTR_TYPE_ID, then *new_value is a char array. */ extern int sc_pkcs15init_change_attrib(struct sc_pkcs15_card *, struct sc_profile *, struct sc_pkcs15_object *, int, void *, int); extern int sc_pkcs15init_add_object(struct sc_pkcs15_card *, struct sc_profile *profile, unsigned int, struct sc_pkcs15_object *); 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 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 *, struct sc_profile *, struct sc_pkcs15_object *, const unsigned char *, size_t); 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 *, 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 *, struct sc_file *); extern int sc_pkcs15init_get_pin_info(struct sc_profile *, int, struct sc_pkcs15_auth_info *); extern int sc_profile_get_pin_retries(struct sc_profile *, int); extern int sc_pkcs15init_get_manufacturer(struct sc_profile *, const char **); extern int sc_pkcs15init_get_serial(struct sc_profile *, const char **); extern int sc_pkcs15init_set_serial(struct sc_profile *, const char *); extern int sc_pkcs15init_verify_secret(struct sc_profile *, struct sc_pkcs15_card *, sc_file_t *, unsigned int, int); extern int sc_pkcs15init_delete_by_path(struct sc_profile *, struct sc_pkcs15_card *, const struct sc_path *); extern int sc_pkcs15init_update_any_df(struct sc_pkcs15_card *, struct sc_profile *, struct sc_pkcs15_df *, int); extern int sc_pkcs15init_select_intrinsic_id(struct sc_pkcs15_card *, struct sc_profile *, int, struct sc_pkcs15_id *, void *); /* Erasing the card structure via rm -rf */ extern int sc_pkcs15init_erase_card_recursively(struct sc_pkcs15_card *, struct sc_profile *); extern int sc_pkcs15init_rmdir(struct sc_pkcs15_card *, struct sc_profile *, struct sc_file *); extern int sc_pkcs15_create_pin_domain(struct sc_profile *, struct sc_pkcs15_card *, const struct sc_pkcs15_id *, struct sc_file **); extern int sc_pkcs15init_get_pin_reference(struct sc_pkcs15_card *, struct sc_profile *, unsigned, int); 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 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_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_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_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); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_piv_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_openpgp_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_sc_hsm_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_isoApplet_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_gids_ops(void); #ifdef __cplusplus } #endif #endif /* PKCS15_INIT_H */ OpenSC-0.26.1/src/pkcs15init/pkcs15-isoApplet.c000066400000000000000000000754361474147347300207550ustar00rootroot00000000000000/* * pkcs15-init driver for JavaCards with IsoApplet installed. * * Copyright (C) 2014 Philip Wendland * * 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 #include "../libopensc/log.h" #include "../libopensc/internal.h" #include "../libopensc/opensc.h" #include "../libopensc/cardctl.h" #include "../libopensc/asn1.h" #include "pkcs15-init.h" #include "profile.h" #define ISOAPPLET_KEY_ID_MIN 0 #define ISOAPPLET_KEY_ID_MAX 15 /* Curve parameters of a curve specified by the OID. */ struct ec_curve { const struct sc_lv_data oid; /* Object ID in hex, including structural information */ const struct sc_lv_data prime; const struct sc_lv_data coefficientA; const struct sc_lv_data coefficientB; const struct sc_lv_data basePointG; const struct sc_lv_data order; const struct sc_lv_data coFactor; }; /* OpenSC only works with named curves, but we need the * explicit parameters for ECC key generation or import. */ static const struct ec_curve curves[] = { { /* brainpoolP192r1 */ { (unsigned char *) "\x06\x09\x2B\x24\x03\x03\x02\x08\x01\x01\x03", 11}, { (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 *) "\x6A\x91\x17\x40\x76\xB1\xE0\xE1\x9C\x39\xC0\x31\xFE\x86\x85\xC1\xCA\xE0\x40\xE5\xC6\x9A\x28\xEF", 24}, { (unsigned char *) "\x46\x9A\x28\xEF\x7C\x28\xCC\xA3\xDC\x72\x1D\x04\x4F\x44\x96\xBC\xCA\x7E\xF4\x14\x6F\xBF\x25\xC9", 24}, { (unsigned char *) "\x04\xC0\xA0\x64\x7E\xAA\xB6\xA4\x87\x53\xB0\x33\xC5\x6C\xB0\xF0\x90\x0A\x2F\x5C\x48\x53\x37\x5F\xD6\x14\xB6\x90\x86\x6A\xBD\x5B\xB8\x8B\x5F\x48\x28\xC1\x49\x00\x02\xE6\x77\x3F\xA2\xFA\x29\x9B\x8F", 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 *) "\x00\x01", 2} }, { /* brainpoolP224r1 */ { (unsigned char *) "\x06\x09\x2B\x24\x03\x03\x02\x08\x01\x01\x05", 11}, { (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 *) "\x68\xA5\xE6\x2C\xA9\xCE\x6C\x1C\x29\x98\x03\xA6\xC1\x53\x0B\x51\x4E\x18\x2A\xD8\xB0\x04\x2A\x59\xCA\xD2\x9F\x43", 28}, { (unsigned char *) "\x25\x80\xF6\x3C\xCF\xE4\x41\x38\x87\x07\x13\xB1\xA9\x23\x69\xE3\x3E\x21\x35\xD2\x66\xDB\xB3\x72\x38\x6C\x40\x0B", 28}, { (unsigned char *) "\x04\x0D\x90\x29\xAD\x2C\x7E\x5C\xF4\x34\x08\x23\xB2\xA8\x7D\xC6\x8C\x9E\x4C\xE3\x17\x4C\x1E\x6E\xFD\xEE\x12\xC0\x7D\x58\xAA\x56\xF7\x72\xC0\x72\x6F\x24\xC6\xB8\x9E\x4E\xCD\xAC\x24\x35\x4B\x9E\x99\xCA\xA3\xF6\xD3\x76\x14\x02\xCD", 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 *) "\x00\x01", 2} }, { /* brainpoolP256r1 */ { (unsigned char *) "\x06\x09\x2B\x24\x03\x03\x02\x08\x01\x01\x07", 11}, { (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 *) "\x7D\x5A\x09\x75\xFC\x2C\x30\x57\xEE\xF6\x75\x30\x41\x7A\xFF\xE7\xFB\x80\x55\xC1\x26\xDC\x5C\x6C\xE9\x4A\x4B\x44\xF3\x30\xB5\xD9", 32}, { (unsigned char *) "\x26\xDC\x5C\x6C\xE9\x4A\x4B\x44\xF3\x30\xB5\xD9\xBB\xD7\x7C\xBF\x95\x84\x16\x29\x5C\xF7\xE1\xCE\x6B\xCC\xDC\x18\xFF\x8C\x07\xB6", 32}, { (unsigned char *) "\x04\x8B\xD2\xAE\xB9\xCB\x7E\x57\xCB\x2C\x4B\x48\x2F\xFC\x81\xB7\xAF\xB9\xDE\x27\xE1\xE3\xBD\x23\xC2\x3A\x44\x53\xBD\x9A\xCE\x32\x62\x54\x7E\xF8\x35\xC3\xDA\xC4\xFD\x97\xF8\x46\x1A\x14\x61\x1D\xC9\xC2\x77\x45\x13\x2D\xED\x8E\x54\x5C\x1D\x54\xC7\x2F\x04\x69\x97", 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 *) "\x00\x01", 2} }, { /* brainpoolP320r1 */ { (unsigned char *) "\x06\x09\x2B\x24\x03\x03\x02\x08\x01\x01\x09", 11}, { (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 *) "\x3E\xE3\x0B\x56\x8F\xBA\xB0\xF8\x83\xCC\xEB\xD4\x6D\x3F\x3B\xB8\xA2\xA7\x35\x13\xF5\xEB\x79\xDA\x66\x19\x0E\xB0\x85\xFF\xA9\xF4\x92\xF3\x75\xA9\x7D\x86\x0E\xB4", 40}, { (unsigned char *) "\x52\x08\x83\x94\x9D\xFD\xBC\x42\xD3\xAD\x19\x86\x40\x68\x8A\x6F\xE1\x3F\x41\x34\x95\x54\xB4\x9A\xCC\x31\xDC\xCD\x88\x45\x39\x81\x6F\x5E\xB4\xAC\x8F\xB1\xF1\xA6", 40}, { (unsigned char *) "\x04\x43\xBD\x7E\x9A\xFB\x53\xD8\xB8\x52\x89\xBC\xC4\x8E\xE5\xBF\xE6\xF2\x01\x37\xD1\x0A\x08\x7E\xB6\xE7\x87\x1E\x2A\x10\xA5\x99\xC7\x10\xAF\x8D\x0D\x39\xE2\x06\x11\x14\xFD\xD0\x55\x45\xEC\x1C\xC8\xAB\x40\x93\x24\x7F\x77\x27\x5E\x07\x43\xFF\xED\x11\x71\x82\xEA\xA9\xC7\x78\x77\xAA\xAC\x6A\xC7\xD3\x52\x45\xD1\x69\x2E\x8E\xE1", 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 *) "\x00\x01", 2} }, { /* prime192v1, secp192r1, ansiX9p192r1 */ { (unsigned char *) "\x06\x08\x2A\x86\x48\xCE\x3D\x03\x01\x01", 10}, { (unsigned char *) "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 24}, { (unsigned char *) "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFC", 24}, { (unsigned char *) "\x64\x21\x05\x19\xE5\x9C\x80\xE7\x0F\xA7\xE9\xAB\x72\x24\x30\x49\xFE\xB8\xDE\xEC\xC1\x46\xB9\xB1", 24}, { (unsigned char *) "\x04\x18\x8D\xA8\x0E\xB0\x30\x90\xF6\x7C\xBF\x20\xEB\x43\xA1\x88\x00\xF4\xFF\x0A\xFD\x82\xFF\x10\x12\x07\x19\x2B\x95\xFF\xC8\xDA\x78\x63\x10\x11\xED\x6B\x24\xCD\xD5\x73\xF9\x77\xA1\x1E\x79\x48\x11", 49}, { (unsigned char *) "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x99\xDE\xF8\x36\x14\x6B\xC9\xB1\xB4\xD2\x28\x31", 24}, { (unsigned char *) "\x00\x01", 2} }, { /* prime224v1, nistp224 */ { (unsigned char *) "\x06\x05\x2b\x81\x04\x00\x21", 7}, { (unsigned char *) "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", 28}, { (unsigned char *) "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE", 28}, { (unsigned char *) "\xB4\x05\x0A\x85\x0C\x04\xB3\xAB\xF5\x41\x32\x56\x50\x44\xB0\xB7\xD7\xBF\xD8\xBA\x27\x0B\x39\x43\x23\x55\xFF\xB4", 28}, { (unsigned char *) "\x04\xB7\x0E\x0C\xBD\x6B\xB4\xBF\x7F\x32\x13\x90\xB9\x4A\x03\xC1\xD3\x56\xC2\x11\x22\x34\x32\x80\xD6\x11\x5C\x1D\x21\xBD\x37\x63\x88\xB5\xF7\x23\xFB\x4C\x22\xDF\xE6\xCD\x43\x75\xA0\x5A\x07\x47\x64\x44\xD5\x81\x99\x85\x00\x7E\x34", 57}, { (unsigned char *) "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x16\xA2\xE0\xB8\xF0\x3E\x13\xDD\x29\x45\x5C\x5C\x2A\x3D", 28}, { (unsigned char *) "\x00\x01", 2} }, { /* prime256v1, secp256r1, ansiX9p256r1 */ { (unsigned char *) "\x06\x08\x2A\x86\x48\xCE\x3D\x03\x01\x07", 10}, { (unsigned char *) "\xFF\xFF\xFF\xFF\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 32}, { (unsigned char *) "\xFF\xFF\xFF\xFF\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFC", 32}, { (unsigned char *) "\x5A\xC6\x35\xD8\xAA\x3A\x93\xE7\xB3\xEB\xBD\x55\x76\x98\x86\xBC\x65\x1D\x06\xB0\xCC\x53\xB0\xF6\x3B\xCE\x3C\x3E\x27\xD2\x60\x4B", 32}, { (unsigned char *) "\x04\x6B\x17\xD1\xF2\xE1\x2C\x42\x47\xF8\xBC\xE6\xE5\x63\xA4\x40\xF2\x77\x03\x7D\x81\x2D\xEB\x33\xA0\xF4\xA1\x39\x45\xD8\x98\xC2\x96\x4F\xE3\x42\xE2\xFE\x1A\x7F\x9B\x8E\xE7\xEB\x4A\x7C\x0F\x9E\x16\x2B\xCE\x33\x57\x6B\x31\x5E\xCE\xCB\xB6\x40\x68\x37\xBF\x51\xF5", 65}, { (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 *) "\x00\x01", 2} }, { /* prime384v1, secp384r1, ansiX9p384r1 */ { (unsigned char *) "\x06\x05\x2B\x81\x04\x00\x22", 7}, { (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 *) "\x00\x01", 2} }, { /* secp192k1 */ { (unsigned char *) "\x06\x05\x2B\x81\x04\x00\x1F", 7}, { (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}, { (unsigned char *) "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 24}, { (unsigned char *) "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03", 24}, { (unsigned char *) "\x04\xDB\x4F\xF1\x0E\xC0\x57\xE9\xAE\x26\xB0\x7D\x02\x80\xB7\xF4\x34\x1D\xA5\xD1\xB1\xEA\xE0\x6C\x7D\x9B\x2F\x2F\x6D\x9C\x56\x28\xA7\x84\x41\x63\xD0\x15\xBE\x86\x34\x40\x82\xAA\x88\xD9\x5E\x2F\x9D", 49}, { (unsigned char *) "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE\x26\xF2\xFC\x17\x0F\x69\x46\x6A\x74\xDE\xFD\x8D", 24}, { (unsigned char *) "\x00\x01", 2} }, { /* secp256k1 */ { (unsigned char *) "\x06\x05\x2B\x81\x04\x00\x0A", 7}, { (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\xFE\xFF\xFF\xFC\x2F", 32}, { (unsigned char *) "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 32}, { (unsigned char *) "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x07", 32}, { (unsigned char *) "\x04\x79\xBE\x66\x7E\xF9\xDC\xBB\xAC\x55\xA0\x62\x95\xCE\x87\x0B\x07\x02\x9B\xFC\xDB\x2D\xCE\x28\xD9\x59\xF2\x81\x5B\x16\xF8\x17\x98\x48\x3A\xDA\x77\x26\xA3\xC4\x65\x5D\xA4\xFB\xFC\x0E\x11\x08\xA8\xFD\x17\xB4\x48\xA6\x85\x54\x19\x9C\x47\xD0\x8F\xFB\x10\xD4\xB8", 65}, { (unsigned char *) "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE\xBA\xAE\xDC\xE6\xAF\x48\xA0\x3B\xBF\xD2\x5E\x8C\xD0\x36\x41\x41", 32}, { (unsigned char *) "\x00\x01", 2} }, { { NULL, 0}, { NULL, 0}, { NULL, 0}, { NULL, 0}, { NULL, 0}, { NULL, 0}, { NULL, 0} } }; /* * Create DF, using default pkcs15init functions. */ static int isoApplet_create_dir(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *df) { sc_card_t *card = p15card->card; int r = SC_SUCCESS; LOG_FUNC_CALLED(card->ctx); if(!profile || !df || !p15card->card->ctx) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } r = sc_pkcs15init_create_file(profile, p15card, df); LOG_FUNC_RETURN(card->ctx, r); } /* * Select a PIN reference. * * Basically (as I understand it) the caller passes an auth_info object and the * auth_info->attrs.pin.reference is supposed to be set accordingly and return. * * The IsoApplet only supports a PIN and a PUK at the moment. * The reference for the PIN is 1, for the PUK 2. */ static int isoApplet_select_pin_reference(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_auth_info_t *auth_info) { sc_card_t *card = p15card->card; int preferred; int current; LOG_FUNC_CALLED(card->ctx); if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_OBJECT_NOT_VALID); } current = auth_info->attrs.pin.reference; if (current < 0) { current = 0; } if(current > 2) { /* Only two PINs supported: User PIN and PUK. */ LOG_FUNC_RETURN(card->ctx, SC_ERROR_TOO_MANY_OBJECTS); } else { if(auth_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN) { /* PUK */ preferred = 2; } else { /* PIN */ preferred = 1; } } auth_info->attrs.pin.reference = preferred; LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } /* * Create a PIN and store it on the card using CHANGE REFERENCE DATA for PIN transmission. * First, the PUK is transmitted, then the PIN. Now, the IsoApplet is in the * "STATE_OPERATIONAL_ACTIVATED" lifecycle state. */ static int isoApplet_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_card_t *card = p15card->card; 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; int r; LOG_FUNC_CALLED(card->ctx); if(!pin || !pin_len || !df) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } if(pin_attrs->reference != 1 && pin_attrs->reference != 2) { /* Reject PIN reference. */ LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_PIN_REFERENCE); } /* If we have a PUK, set it first. */ if(puk && puk_len) { /* The PUK has a incremented reference, i.e. pins are odd, puks are equal (+1). */ r = sc_change_reference_data(p15card->card, SC_AC_CHV, pin_attrs->reference+1, NULL, 0, puk, puk_len, NULL); if(r < 0) { LOG_FUNC_RETURN(card->ctx, r); } } /* Store PIN: (use CHANGE REFERENCE DATA). */ r = sc_change_reference_data(p15card->card, SC_AC_CHV, pin_attrs->reference, NULL, 0, pin, pin_len, NULL); LOG_TEST_RET(card->ctx, r, "Failed to set PIN"); sc_pkcs15_pincache_add(p15card, pin_obj, pin, pin_len); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } /* * @brief Get the curve parameters associated with the curve specified by an OID. * * @param[in] oid The DER encoded OID of the curve. * @param[in] oid_len The length of oid. * @param[out] curve_out The ec_curve containing the set of parameters. * * @returns SC_SUCCESS: If the curve was found. * SC_ERROR_INVALID_ARGUMENTS: If named_curve was null or the curve * was not found */ static int isoApplet_get_curve(u8 *oid, size_t oid_len, const struct ec_curve **curve_out) { int i; if(!oid) return SC_ERROR_INVALID_ARGUMENTS; /* Search the curve parameters. */ for (i = 0; curves[i].oid.value; i++) { if (oid_len == curves[i].oid.len && memcmp(oid, curves[i].oid.value, curves[i].oid.len) == 0) { *curve_out = &curves[i]; return SC_SUCCESS; } } return SC_ERROR_INVALID_ARGUMENTS; } /* * @brief Generate a RSA private key on the card. * * A MANAGE SECURITY ENVIRONMENT apdu must have been sent before. * This function uses card_ctl to access the card-isoApplet driver. * * @param[in] key_info * @param[in] card * @param[in] pubkey The public key of the generated key pair * returned by the card. * * @return SC_ERROR_INVALID_ARGUMENTS: Invalid key length. * SC_ERROR_OUT_OF_MEMORY */ static int isoApplet_generate_key_rsa(sc_pkcs15_prkey_info_t *key_info, sc_card_t *card, sc_pkcs15_pubkey_t *pubkey) { int rv; size_t keybits; struct sc_cardctl_isoApplet_genkey args; LOG_FUNC_CALLED(card->ctx); memset(&args, 0, sizeof(args)); /* Check key size: */ keybits = key_info->modulus_length; 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 or 4096", sc_strerror(rv)); goto err; } /* Generate the key. * Note: key size is not explicitly passed to the card. * 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; args.pubkey.rsa.modulus.value = malloc(args.pubkey.rsa.modulus.len); if (!args.pubkey.rsa.modulus.value) { rv = SC_ERROR_OUT_OF_MEMORY; sc_log(card->ctx, "%s: Unable to allocate public key buffer.", sc_strerror(rv)); goto err; } args.pubkey.rsa.exponent.len = 3; args.pubkey.rsa.exponent.value = malloc(args.pubkey.rsa.exponent.len); if(!args.pubkey.rsa.exponent.value) { rv = SC_ERROR_OUT_OF_MEMORY; sc_log(card->ctx, "%s: Unable to allocate public key exponent buffer.", sc_strerror(rv)); goto err; } rv = sc_card_ctl(card, SC_CARDCTL_ISOAPPLET_GENERATE_KEY, &args); if (rv < 0) { sc_log(card->ctx, "%s: Error in card_ctl", sc_strerror(rv)); goto err; } /* extract the public key */ pubkey->algorithm = SC_ALGORITHM_RSA; pubkey->u.rsa.modulus.len = args.pubkey.rsa.modulus.len; pubkey->u.rsa.modulus.data = args.pubkey.rsa.modulus.value; pubkey->u.rsa.exponent.len = args.pubkey.rsa.exponent.len; pubkey->u.rsa.exponent.data = args.pubkey.rsa.exponent.value; rv = SC_SUCCESS; LOG_FUNC_RETURN(card->ctx, rv); err: if (args.pubkey.rsa.modulus.value) { free(args.pubkey.rsa.modulus.value); pubkey->u.rsa.modulus.data = NULL; pubkey->u.rsa.modulus.len = 0; } if (args.pubkey.rsa.exponent.value) { free(args.pubkey.rsa.exponent.value); pubkey->u.rsa.exponent.data = NULL; pubkey->u.rsa.exponent.len = 0; } LOG_FUNC_RETURN(card->ctx, rv); } /* * @brief Generate a EC private key on the card. * * A MANAGE SECURITY ENVIRONMENT apdu must have been sent before. * This function uses card_ctl to access the card-isoApplet driver. * * @param[in] key_info * @param[in] card * @param[in/out] pubkey The public key of the generated key pair * returned by the card. * * @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 * handled. */ static int isoApplet_generate_key_ec(const sc_pkcs15_prkey_info_t *key_info, sc_card_t *card, sc_pkcs15_pubkey_t *pubkey) { int r; const struct ec_curve *curve = NULL; struct sc_ec_parameters *alg_id_params = NULL; sc_cardctl_isoApplet_genkey_t args; const struct sc_ec_parameters *info_ecp = (struct sc_ec_parameters *) key_info->params.data; LOG_FUNC_CALLED(card->ctx); memset(&args, 0, sizeof(args)); /* Check key size: */ if(key_info->field_length == 0) { sc_log(card->ctx, "Unknown field length."); r = SC_ERROR_INVALID_ARGUMENTS; goto out; } r = isoApplet_get_curve(info_ecp->der.value, info_ecp->der.len, &curve); if(r < 0) { sc_log(card->ctx, "EC key generation failed: Unsupported curve: [%s].", info_ecp->named_curve); goto out; } /* 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. */ 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; args.pubkey.ec.params.coefficientA.len = curve->coefficientA.len; args.pubkey.ec.params.coefficientB.value = curve->coefficientB.value; args.pubkey.ec.params.coefficientB.len = curve->coefficientB.len; args.pubkey.ec.params.basePointG.value = curve->basePointG.value; args.pubkey.ec.params.basePointG.len = curve->basePointG.len; args.pubkey.ec.params.order.value = curve->order.value; args.pubkey.ec.params.order.len = curve->order.len; args.pubkey.ec.params.coFactor.value = curve->coFactor.value; 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.value = malloc(args.pubkey.ec.ecPointQ.len); if(!args.pubkey.ec.ecPointQ.value) { r = SC_ERROR_OUT_OF_MEMORY; goto out; } args.algorithm_ref = SC_ISOAPPLET_ALG_REF_EC_GEN; args.priv_key_ref = key_info->key_reference; /* On-card key generation */ r = sc_card_ctl(card, SC_CARDCTL_ISOAPPLET_GENERATE_KEY, &args); if (r < 0) { sc_log(card->ctx, "%s: Error in card_ctl.", sc_strerror(r)); goto out; } /* Extract and compose the public key. */ pubkey->algorithm = SC_ALGORITHM_EC; /* der-encoded parameters */ alg_id_params = calloc(1, sizeof(*alg_id_params)); if(!alg_id_params) { r = SC_ERROR_OUT_OF_MEMORY; goto out; } alg_id_params->der.len = curve->oid.len; alg_id_params->der.value = malloc(alg_id_params->der.len); if(!alg_id_params->der.value) { r = SC_ERROR_OUT_OF_MEMORY; goto out; } memcpy(alg_id_params->der.value, curve->oid.value, curve->oid.len); alg_id_params->type = 1; /* named curve */ pubkey->alg_id = malloc(sizeof(*pubkey->alg_id)); if(!pubkey->alg_id) { r = SC_ERROR_OUT_OF_MEMORY; 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 */ pubkey->u.ec.ecpointQ.len = args.pubkey.ec.ecPointQ.len; pubkey->u.ec.ecpointQ.value = malloc(pubkey->u.ec.ecpointQ.len); if(!pubkey->u.ec.ecpointQ.value) { r = SC_ERROR_OUT_OF_MEMORY; goto out; } 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) { r = SC_ERROR_OUT_OF_MEMORY; goto out; } memcpy(pubkey->u.ec.params.der.value, alg_id_params->der.value, alg_id_params->der.len); pubkey->u.ec.params.der.len = alg_id_params->der.len; r = sc_pkcs15_fix_ec_parameters(card->ctx, &pubkey->u.ec.params); out: if(args.pubkey.ec.ecPointQ.value) { free(args.pubkey.ec.ecPointQ.value); args.pubkey.ec.ecPointQ.value = NULL; } if(r < 0 && pubkey) { if(pubkey->alg_id) { free(pubkey->alg_id); pubkey->alg_id = NULL; } if(pubkey->u.ec.params.der.value) { free(pubkey->u.ec.params.der.value); pubkey->u.ec.params.der.value = NULL; pubkey->u.ec.params.der.len = 0; } 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; pubkey->u.ec.ecpointQ.len = 0; } memset(pubkey, 0, sizeof(sc_pkcs15_pubkey_t)); } if(r < 0 && alg_id_params) { if(alg_id_params->der.value) { free(alg_id_params->der.value); alg_id_params->der.value = NULL; } free(alg_id_params); pubkey->alg_id->params = NULL; } LOG_FUNC_RETURN(card->ctx, r); } static int isoApplet_generate_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *obj, sc_pkcs15_pubkey_t *pubkey) { int r; sc_pkcs15_prkey_info_t *key_info = (sc_pkcs15_prkey_info_t *) obj->data; sc_file_t *privKeyFile=NULL; sc_card_t *card = p15card->card; LOG_FUNC_CALLED(card->ctx); /* Authentication stuff. */ r = sc_profile_get_file_by_path(profile, &key_info->path, &privKeyFile); if(r < 0 || !privKeyFile) { SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_SUPPORTED); } r = sc_pkcs15init_authenticate(profile, p15card, privKeyFile, SC_AC_OP_CREATE_EF); if(r < 0) { sc_file_free(privKeyFile); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_SUPPORTED); } sc_file_free(privKeyFile); /* Generate the key. */ switch(obj->type) { case SC_PKCS15_TYPE_PRKEY_RSA: r = isoApplet_generate_key_rsa(key_info, card, pubkey); break; case SC_PKCS15_TYPE_PRKEY_EC: r = isoApplet_generate_key_ec(key_info, card, 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(card->ctx, r); } /* * Create a new key file. This is a no-op, because private keys are stored as key objects on the javacard. */ static int isoApplet_create_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *obj) { sc_card_t *card = p15card->card; LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } /* * Select a key reference. */ static int isoApplet_select_key_reference(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_prkey_info_t *key_info) { int rv = SC_SUCCESS; sc_card_t *card = p15card->card; LOG_FUNC_CALLED(card->ctx); if(key_info->key_reference < ISOAPPLET_KEY_ID_MIN) { key_info->key_reference = ISOAPPLET_KEY_ID_MIN; rv = SC_SUCCESS; } if(key_info->key_reference > ISOAPPLET_KEY_ID_MAX) { rv = SC_ERROR_TOO_MANY_OBJECTS; } LOG_FUNC_RETURN(card->ctx, rv); } /* * Store a usable private key on the card. */ static int isoApplet_store_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *object, sc_pkcs15_prkey_t *key) { sc_card_t *card = p15card->card; sc_pkcs15_prkey_info_t *key_info = (sc_pkcs15_prkey_info_t *) object->data; sc_file_t *privKeyFile=NULL; sc_cardctl_isoApplet_import_key_t args; int r; LOG_FUNC_CALLED(card->ctx); /* Authentication stuff. */ r = sc_profile_get_file_by_path(profile, &key_info->path, &privKeyFile); if(r < 0 || !privKeyFile) { SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_SUPPORTED); } r = sc_pkcs15init_authenticate(profile, p15card, privKeyFile, SC_AC_OP_CREATE_EF); if(r < 0) { sc_file_free(privKeyFile); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_SUPPORTED); } sc_file_free(privKeyFile); /* Key import. */ switch(object->type) { case SC_PKCS15_TYPE_PRKEY_RSA: args.algorithm_ref = SC_ISOAPPLET_ALG_REF_RSA_GEN_2048; if(!key->u.rsa.p.data ||!key->u.rsa.q.data ||!key->u.rsa.iqmp.data ||!key->u.rsa.dmp1.data ||!key->u.rsa.dmq1.data) { LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "Only CRT RSA keys may be imported."); } args.privkey.rsa.p.value = key->u.rsa.p.data; args.privkey.rsa.p.len = key->u.rsa.p.len; args.privkey.rsa.q.value = key->u.rsa.q.data; args.privkey.rsa.q.len = key->u.rsa.q.len; args.privkey.rsa.iqmp.value = key->u.rsa.iqmp.data; args.privkey.rsa.iqmp.len = key->u.rsa.iqmp.len; args.privkey.rsa.dmp1.value = key->u.rsa.dmp1.data; args.privkey.rsa.dmp1.len = key->u.rsa.dmp1.len; args.privkey.rsa.dmq1.value = key->u.rsa.dmq1.data; args.privkey.rsa.dmq1.len = key->u.rsa.dmq1.len; break; case SC_PKCS15_TYPE_PRKEY_EC: { const struct ec_curve *curve = NULL; 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: 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"); args.privkey.ec.params.prime.value = curve->prime.value; args.privkey.ec.params.prime.len = curve->prime.len; args.privkey.ec.params.coefficientA.value = curve->coefficientA.value; args.privkey.ec.params.coefficientA.len = curve->coefficientA.len; args.privkey.ec.params.coefficientB.value = curve->coefficientB.value; args.privkey.ec.params.coefficientB.len = curve->coefficientB.len; args.privkey.ec.params.basePointG.value = curve->basePointG.value; args.privkey.ec.params.basePointG.len = curve->basePointG.len; args.privkey.ec.params.order.value = curve->order.value; args.privkey.ec.params.order.len = curve->order.len; args.privkey.ec.params.coFactor.value = curve->coFactor.value; args.privkey.ec.params.coFactor.len = curve->coFactor.len; args.privkey.ec.privateD.value = key->u.ec.privateD.data; args.privkey.ec.privateD.len = key->u.ec.privateD.len; } break; default: LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } args.priv_key_ref = key_info->key_reference; r = sc_card_ctl(card, SC_CARDCTL_ISOAPPLET_IMPORT_KEY, &args); if (r < 0) { sc_log(card->ctx, "%s: Error in card_ctl", sc_strerror(r)); LOG_FUNC_RETURN(card->ctx, r); } LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } static struct sc_pkcs15init_operations sc_pkcs15init_isoApplet_operations = { NULL, /* erase_card */ NULL, /* init_card */ isoApplet_create_dir, /* create_dir */ NULL, /* create_domain */ isoApplet_select_pin_reference, /* pin_reference*/ isoApplet_create_pin, /* create_pin */ isoApplet_select_key_reference, /* key_reference */ isoApplet_create_key, /* create_key */ isoApplet_store_key, /* store_key */ isoApplet_generate_key, /* generate_key */ NULL, NULL, /* encode private/public key */ NULL, /* finalize */ NULL, /* delete_object */ NULL, NULL, NULL, NULL, NULL, /* pkcs15init emulation */ NULL, /* sanity_check*/ }; struct sc_pkcs15init_operations *sc_pkcs15init_get_isoApplet_ops(void) { return &sc_pkcs15init_isoApplet_operations; } OpenSC-0.26.1/src/pkcs15init/pkcs15-lib.c000066400000000000000000004211261474147347300175520ustar00rootroot00000000000000/* * Initialize Cards according to PKCS#15. * * This is a fill in the blanks sort of exercise. You need a * profile that describes characteristics of your card, and the * application specific layout on the card. This program will * set up the card according to this specification (including * PIN initialization etc) and create the corresponding PKCS15 * structure. * * There are a very few tasks that are too card specific to have * a generic implementation; that is how PINs and keys are stored * on the card. These should be implemented in pkcs15-.c * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include #include #include #include #include #include #include #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_STRINGS_H #include #endif #include #ifdef ENABLE_OPENSSL #include #include #include #include #include #include #include #include #endif #include "libopensc/sc-ossl-compat.h" #include "common/compat_strlcpy.h" #include "common/libscdl.h" #include "libopensc/pkcs15.h" #include "libopensc/cardctl.h" #include "libopensc/asn1.h" #include "libopensc/log.h" #include "libopensc/aux-data.h" #include "profile.h" #include "pkcs15-init.h" #include "pkcs11/pkcs11.h" #define OPENSC_INFO_FILEPATH "3F0050154946" #define OPENSC_INFO_FILEID 0x4946 #define OPENSC_INFO_TAG_PROFILE 0x01 #define OPENSC_INFO_TAG_OPTION 0x02 /* Default ID for new key/pin */ #define DEFAULT_ID 0x45 #define DEFAULT_PIN_FLAGS (SC_PKCS15_CO_FLAG_PRIVATE|SC_PKCS15_CO_FLAG_MODIFIABLE) #define DEFAULT_PRKEY_FLAGS (SC_PKCS15_CO_FLAG_PRIVATE|SC_PKCS15_CO_FLAG_MODIFIABLE) #define DEFAULT_PUBKEY_FLAGS (SC_PKCS15_CO_FLAG_MODIFIABLE) #define DEFAULT_SKEY_FLAGS (SC_PKCS15_CO_FLAG_PRIVATE|SC_PKCS15_CO_FLAG_MODIFIABLE) #define DEFAULT_CERT_FLAGS (SC_PKCS15_CO_FLAG_MODIFIABLE) #define DEFAULT_DATA_FLAGS (SC_PKCS15_CO_FLAG_MODIFIABLE) #define TEMPLATE_INSTANTIATE_MIN_INDEX 0x0 #define TEMPLATE_INSTANTIATE_MAX_INDEX 0xFE /* Maximal number of access conditions that can be defined for one card operation. */ #define SC_MAX_OP_ACS 16 /* Handle encoding of PKCS15 on the card */ typedef int (*pkcs15_encoder)(struct sc_context *, struct sc_pkcs15_card *, u8 **, size_t *); static int sc_pkcs15init_store_data(struct sc_pkcs15_card *, struct sc_profile *, struct sc_pkcs15_object *, struct sc_pkcs15_der *, struct sc_path *); static size_t sc_pkcs15init_keybits(struct sc_pkcs15_bignum *); static int sc_pkcs15init_update_dir(struct sc_pkcs15_card *, struct sc_profile *profile, struct sc_app_info *app); static int sc_pkcs15init_update_tokeninfo(struct sc_pkcs15_card *, struct sc_profile *profile); 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 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 long alg, struct sc_pkcs15init_prkeyargs *prkey, unsigned int *keybits); 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 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 *, 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 *); static int sc_pkcs15init_parse_info(struct sc_card *, const unsigned char *, size_t, struct sc_profile *); static int sc_pkcs15init_write_info(struct sc_pkcs15_card *, struct sc_profile *, struct sc_pkcs15_object *); static struct profile_operations { const char *name; void *func; } profile_operations[] = { { "rutoken", (void *) sc_pkcs15init_get_rutoken_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 */ { "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 }, { "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 }, { "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 }, { "gids", (void *) sc_pkcs15init_get_gids_ops }, #ifdef ENABLE_OPENSSL { "authentic", (void *) sc_pkcs15init_get_authentic_ops }, { "iasecc", (void *) sc_pkcs15init_get_iasecc_ops }, #endif { NULL, NULL }, }; static struct sc_pkcs15init_callbacks callbacks = { NULL, NULL, }; static void sc_pkcs15init_free_ec_params(void *ptr) { struct sc_ec_parameters *ecparams = (struct sc_ec_parameters *)ptr; if (ecparams) { if (ecparams->der.value) free(ecparams->der.value); if (ecparams->named_curve) free(ecparams->named_curve); free(ecparams); } } /* * Set the application callbacks */ void sc_pkcs15init_set_callbacks(struct sc_pkcs15init_callbacks *cb) { callbacks.get_pin = cb? cb->get_pin : NULL; callbacks.get_key = cb? cb->get_key : NULL; } /* * Returns 1 if the a profile was found in the card's card_driver block * in the config file, or 0 otherwise. */ static int get_profile_from_config(struct sc_card *card, char *buffer, size_t size) { struct sc_context *ctx = card->ctx; const char *tmp; scconf_block **blocks, *blk; int i; for (i = 0; ctx->conf_blocks[i]; i++) { blocks = scconf_find_blocks(ctx->conf, ctx->conf_blocks[i], "card_driver", card->driver->short_name); if (!blocks) continue; blk = blocks[0]; free(blocks); if (blk == NULL) continue; tmp = scconf_get_str(blk, "profile", NULL); if (tmp != NULL) { strlcpy(buffer, tmp, size); return 1; } } return 0; } static const char * find_library(struct sc_context *ctx, const char *name) { int i; const char *libname = NULL; scconf_block *blk, **blocks; for (i = 0; ctx->conf_blocks[i]; i++) { blocks = scconf_find_blocks(ctx->conf, ctx->conf_blocks[i], "framework", "pkcs15"); if (!blocks) continue; blk = blocks[0]; free(blocks); if (blk == NULL) continue; blocks = scconf_find_blocks(ctx->conf, blk, "pkcs15init", name); if (!blocks) continue; blk = blocks[0]; free(blocks); if (blk == NULL) continue; libname = scconf_get_str(blk, "module", NULL); break; } if (!libname) { sc_log(ctx, "unable to locate pkcs15init driver for '%s'", name); } return libname; } static void * load_dynamic_driver(struct sc_context *ctx, void **dll, const char *name) { const char *version, *libname; void *handle; void *(*modinit)(const char *) = NULL; const char *(*modversion)(void) = NULL; libname = find_library(ctx, name); if (!libname) return NULL; handle = sc_dlopen(libname); if (handle == NULL) { sc_log(ctx, "Module %s: cannot load '%s' library: %s", name, libname, sc_dlerror()); return NULL; } /* verify correctness of module */ modinit = (void *(*)(const char *)) sc_dlsym(handle, "sc_module_init"); modversion = (const char *(*)(void)) sc_dlsym(handle, "sc_driver_version"); if (modinit == NULL || modversion == NULL) { sc_log(ctx, "dynamic library '%s' is not a OpenSC module",libname); sc_dlclose(handle); return NULL; } /* verify module version */ version = modversion(); if (version == NULL || strncmp(version, "0.9.", strlen("0.9.")) > 0) { sc_log(ctx,"dynamic library '%s': invalid module version",libname); sc_dlclose(handle); return NULL; } *dll = handle; sc_log(ctx, "successfully loaded pkcs15init driver '%s'", name); return modinit(name); } /* * Set up profile */ int sc_pkcs15init_bind(struct sc_card *card, const char *name, const char *profile_option, struct sc_app_info *app_info, struct sc_profile **result) { struct sc_context *ctx = card->ctx; struct sc_profile *profile; struct sc_pkcs15init_operations * (* func)(void) = NULL; const char *driver = card->driver->short_name; char card_profile[PATH_MAX]; int r, i; LOG_FUNC_CALLED(ctx); /* Put the card into administrative mode */ r = sc_pkcs15init_set_lifecycle(card, SC_CARDCTRL_LIFECYCLE_ADMIN); if (r < 0 && r != SC_ERROR_NOT_SUPPORTED) LOG_TEST_RET(ctx, r, "Set lifecycle error"); profile = sc_profile_new(); profile->card = card; for (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) { /* no builtin support for this driver => look if there's a * dynamic module for this card */ func = (struct sc_pkcs15init_operations *(*)(void)) load_dynamic_driver(card->ctx, &profile->dll, driver); } if (func) { profile->ops = func(); } else { sc_log(ctx, "Unsupported card driver %s", driver); sc_profile_free(profile); LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Unsupported card driver"); } /* Massage the main profile name to see if there are * any options in there */ profile->name = strdup(name); if (strchr(profile->name, '+') != NULL) { char *s; i = 0; (void) strtok(profile->name, "+"); while ((s = strtok(NULL, "+")) != NULL) { if (i < SC_PKCS15INIT_MAX_OPTIONS-1) profile->options[i++] = strdup(s); } } r = sc_pkcs15init_read_info(card, profile); if (r < 0) { sc_profile_free(profile); LOG_TEST_RET(ctx, r, "Read info error"); } /* Check the config file for a profile name. * If none is defined, use the default profile name. */ if (!get_profile_from_config(card, card_profile, sizeof(card_profile))) strlcpy(card_profile, driver, sizeof card_profile); if (profile_option != NULL) strlcpy(card_profile, profile_option, sizeof(card_profile)); do { r = sc_profile_load(profile, profile->name); if (r < 0) { sc_log(ctx, "Failed to load profile '%s': %s", profile->name, sc_strerror(r)); break; } r = sc_profile_load(profile, card_profile); if (r < 0) { sc_log(ctx, "Failed to load profile '%s': %s", card_profile, sc_strerror(r)); break; } r = sc_profile_finish(profile, app_info); if (r < 0) sc_log(ctx, "Failed to finalize profile: %s", sc_strerror(r)); } while (0); if (r < 0) { sc_profile_free(profile); LOG_TEST_RET(ctx, r, "Load profile error"); } if (app_info && app_info->aid.len) { struct sc_path path; if (card->ef_atr && card->ef_atr->aid.len) { sc_log(ctx, "sc_pkcs15init_bind() select MF using EF.ATR data"); memset(&path, 0, sizeof(struct sc_path)); path.type = SC_PATH_TYPE_DF_NAME; path.aid = card->ef_atr->aid; r = sc_select_file(card, &path, NULL); if (r) return r; } if (app_info->path.len) { path = app_info->path; } else { memset(&path, 0, sizeof(struct sc_path)); path.type = SC_PATH_TYPE_DF_NAME; path.aid = app_info->aid; } sc_log(ctx, "sc_pkcs15init_bind() select application path(type:%X) '%s'", path.type, sc_print_path(&path)); r = sc_select_file(card, &path, NULL); } *result = profile; LOG_FUNC_RETURN(ctx, r); } void sc_pkcs15init_unbind(struct sc_profile *profile) { int r; struct sc_context *ctx = profile->card->ctx; LOG_FUNC_CALLED(ctx); sc_log(ctx, "Pksc15init Unbind: %i:%p:%i", profile->dirty, profile->p15_data, profile->pkcs15.do_last_update); if (profile->dirty != 0 && profile->p15_data != NULL && profile->pkcs15.do_last_update) { r = sc_pkcs15init_update_lastupdate(profile->p15_data, 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); } void sc_pkcs15init_set_p15card(struct sc_profile *profile, struct sc_pkcs15_card *p15card) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_object *p15objects[10]; int i, r, nn_objs; LOG_FUNC_CALLED(ctx); /* Prepare pin-domain instantiation: * for every present local User PIN, add to the profile EF list the named PIN path. */ nn_objs = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_AUTH_PIN, p15objects, 10); for (i = 0; i < nn_objs; i++) { struct sc_pkcs15_auth_info *auth_info = (struct sc_pkcs15_auth_info *) p15objects[i]->data; struct sc_pkcs15_pin_attributes *pin_attrs = &auth_info->attrs.pin; struct sc_file *file = NULL; if (pin_attrs->flags & SC_PKCS15_PIN_FLAG_SO_PIN) continue; if (pin_attrs->flags & SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN) continue; if (!auth_info->path.len) continue; r = sc_profile_get_file_by_path(profile, &auth_info->path, &file); if (r == SC_ERROR_FILE_NOT_FOUND) { if (!sc_select_file(p15card->card, &auth_info->path, &file)) { char pin_name[16]; sprintf(pin_name, "pin-dir-%02X%02X", file->path.value[file->path.len - 2], file->path.value[file->path.len - 1]); sc_log(ctx, "add '%s' to profile file list", pin_name); sc_profile_add_file(profile, pin_name, file); } } sc_file_free(file); } profile->p15_data = p15card; sc_log(ctx, "sc_pkcs15init_set_p15card() returns"); } /* * Set the card's lifecycle */ int sc_pkcs15init_set_lifecycle(struct sc_card *card, int lcycle) { return sc_card_ctl(card, SC_CARDCTL_LIFECYCLE_SET, &lcycle); } /* * Erase the card */ int sc_pkcs15init_erase_card(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_aid *aid) { struct sc_context *ctx = NULL; int rv; if (!p15card) return SC_ERROR_INVALID_ARGUMENTS; ctx = p15card->card->ctx; LOG_FUNC_CALLED(ctx); /* Needs the 'SOPIN' AUTH pkcs15 object. * So that, SOPIN can be found by it's reference. */ if (sc_pkcs15_bind(p15card->card, aid, &p15card) >= 0) profile->p15_data = p15card; if (profile->ops->erase_card == NULL) LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); rv = profile->ops->erase_card(profile, p15card); LOG_FUNC_RETURN(ctx, rv); } int sc_pkcs15init_erase_card_recursively(struct sc_pkcs15_card *p15card, struct sc_profile *profile) { struct sc_file *df = profile->df_info->file, *dir; int r; /* Delete EF(DIR). This may not be very nice * against other applications that use this file, but * extremely useful for testing :) * Note we need to delete it before the DF because we create * it *after* the DF. Some cards (e.g. the cryptoflex) want * us to delete files in reverse order of creation. * */ if (sc_profile_get_file(profile, "DIR", &dir) >= 0) { r = sc_pkcs15init_rmdir(p15card, profile, dir); sc_file_free(dir); if (r < 0 && r != SC_ERROR_FILE_NOT_FOUND) { sc_free_apps(p15card->card); return r; } } r = sc_select_file(p15card->card, &df->path, &df); if (r >= 0) { r = sc_pkcs15init_rmdir(p15card, profile, df); sc_file_free(df); } if (r == SC_ERROR_FILE_NOT_FOUND) r = 0; sc_free_apps(p15card->card); return r; } int sc_pkcs15init_delete_by_path(struct sc_profile *profile, struct sc_pkcs15_card *p15card, const struct sc_path *file_path) { struct sc_context *ctx = p15card->card->ctx; struct sc_file *parent = NULL, *file = NULL; struct sc_path path; int rv; /*int file_type = SC_FILE_TYPE_DF;*/ LOG_FUNC_CALLED(ctx); sc_log(ctx, "trying to delete '%s'", sc_print_path(file_path)); /* For some cards, to delete file should be satisfied the 'DELETE' ACL of the file itself, * for the others the 'DELETE' ACL of parent. * Let's start from the file's 'DELETE' ACL. * * TODO: 'DELETE_SELF' exists. Proper solution would be to use this acl by every * 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"); if (sc_file_get_acl_entry(file, SC_AC_OP_DELETE_SELF)) { sc_log(ctx, "Found 'DELETE-SELF' acl"); rv = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_DELETE_SELF); sc_file_free(file); } else if (sc_file_get_acl_entry(file, SC_AC_OP_DELETE)) { sc_log(ctx, "Found 'DELETE' acl"); rv = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_DELETE); sc_file_free(file); } else { sc_log(ctx, "Try to get the parent's 'DELETE' access"); /*file_type = file->type;*/ if (file_path->len >= 2) { /* 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"); /* Reselect file to delete: current path could be changed by 'verify PIN' procedure */ path = *file_path; rv = sc_select_file(p15card->card, &path, &file); LOG_TEST_RET(ctx, rv, "cannot select file to delete"); 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; /* Reselect file to delete if the parent DF was selected and it's not DF. */ /* if (file_type != SC_FILE_TYPE_DF) { rv = sc_select_file(p15card->card, &path, &file); LOG_TEST_RET(ctx, rv, "cannot select file to delete"); } */ sc_log(ctx, "Now really delete file"); rv = sc_delete_file(p15card->card, &path); sc_file_free(file); LOG_FUNC_RETURN(ctx, rv); } /* * Try to delete a file (and, in the DF case, its contents). * Note that this will not work if a pkcs#15 file's ERASE AC * references a pin other than the SO pin. */ int sc_pkcs15init_rmdir(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_file *df) { struct sc_context *ctx = p15card->card->ctx; unsigned char buffer[1024]; struct sc_path path; struct sc_file *file, *parent; int r = 0, nfids; if (df == NULL) return SC_ERROR_INTERNAL; sc_log(ctx, "sc_pkcs15init_rmdir(%s)", sc_print_path(&df->path)); if (df->type == SC_FILE_TYPE_DF) { r = sc_pkcs15init_authenticate(profile, p15card, df, SC_AC_OP_LIST_FILES); if (r < 0) return r; r = sc_list_files(p15card->card, buffer, sizeof(buffer)); if (r < 0) return r; path = df->path; path.len += 2; if (path.len > SC_MAX_PATH_SIZE) return SC_ERROR_INTERNAL; nfids = r / 2; while (r >= 0 && nfids--) { path.value[path.len-2] = buffer[2*nfids]; path.value[path.len-1] = buffer[2*nfids+1]; r = sc_select_file(p15card->card, &path, &file); if (r < 0) { if (r == SC_ERROR_FILE_NOT_FOUND) continue; break; } r = sc_pkcs15init_rmdir(p15card, profile, file); sc_file_free(file); } if (r < 0) return r; } /* 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) return r; r = sc_pkcs15init_authenticate(profile, p15card, df, SC_AC_OP_DELETE); if (r < 0) { sc_file_free(parent); return r; } r = sc_pkcs15init_authenticate(profile, p15card, parent, SC_AC_OP_DELETE); sc_file_free(parent); if (r < 0) return r; memset(&path, 0, sizeof(path)); path.type = SC_PATH_TYPE_FILE_ID; path.value[0] = df->id >> 8; path.value[1] = df->id & 0xFF; path.len = 2; /* ensure that the card is in the correct lifecycle */ r = sc_pkcs15init_set_lifecycle(p15card->card, SC_CARDCTRL_LIFECYCLE_ADMIN); if (r < 0 && r != SC_ERROR_NOT_SUPPORTED) return r; r = sc_delete_file(p15card->card, &path); return r; } int sc_pkcs15init_finalize_card(struct sc_card *card, struct sc_profile *profile) { if (profile->ops->finalize_card == NULL) return SC_ERROR_NOT_SUPPORTED; return profile->ops->finalize_card(card); } int sc_pkcs15init_finalize_profile(struct sc_card *card, struct sc_profile *profile, struct sc_aid *aid) { struct sc_context *ctx = card->ctx; const struct sc_app_info *app = NULL; int rv; LOG_FUNC_CALLED(ctx); 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)); app = sc_find_app(card, aid); } else if (card->app_count == 1) { app = card->app[0]; } else if (card->app_count > 1) { LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Need AID defined in this context"); } sc_log(ctx, "Finalize profile with application '%s'", app ? app->label : "default"); rv = sc_profile_finish(profile, app); sc_log(ctx, "sc_pkcs15init_finalize_profile() returns %i", rv); LOG_FUNC_RETURN(ctx, rv); } /* * Initialize the PKCS#15 application */ int sc_pkcs15init_add_app(struct sc_card *card, struct sc_profile *profile, struct sc_pkcs15init_initargs *args) { struct sc_context *ctx = card->ctx; struct sc_pkcs15_card *p15card = profile->p15_spec; struct sc_pkcs15_auth_info pin_ainfo, puk_ainfo; struct sc_pkcs15_pin_attributes *pin_attrs = &pin_ainfo.attrs.pin; struct sc_pkcs15_object *pin_obj = NULL; 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; /* FIXME: * Some cards need pincache * for ex. to create temporary CHV key with the value of default AUTH key. */ p15card->opts.use_pin_cache = 1; 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 (has_so_pin) { const char *pin_label; 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) pin_ainfo.path = df->path; /* Select the PIN reference */ if (profile->ops->select_pin_reference) { r = profile->ops->select_pin_reference(profile, p15card, &pin_ainfo); LOG_TEST_RET(ctx, r, "Failed to select card specific PIN reference"); } 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 qualify SO PUK"); if (!(pin_label = args->so_pin_label)) { if (pin_attrs->flags & SC_PKCS15_PIN_FLAG_SO_PIN) pin_label = "Security Officer PIN"; else pin_label = "User PIN"; } if (args->so_puk_len == 0) pin_attrs->flags |= SC_PKCS15_PIN_FLAG_UNBLOCK_DISABLED; pin_obj = sc_pkcs15init_new_object(SC_PKCS15_TYPE_AUTH_PIN, pin_label, NULL, &pin_ainfo); if (pin_obj) { /* When composing ACLs to create 'DIR' DF, * the references of the not-yet-existing PINs can be requested. * For this, create a 'virtual' AUTH object 'SO PIN', accessible by the card specific part, * but not yet written into the on-card PKCS#15. */ sc_log(ctx, "Add virtual SO_PIN('%.*s',flags:%X,reference:%i,path:'%s')", (int) sizeof pin_obj->label, pin_obj->label, pin_attrs->flags, pin_attrs->reference, sc_print_path(&pin_ainfo.path)); r = sc_pkcs15_add_object(p15card, pin_obj); 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); } LOG_TEST_GOTO_ERR(ctx, r, "Card specific init failed"); } /* Create the application directory */ if (profile->ops->create_dir) { r = profile->ops->create_dir(profile, p15card, df); 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) r = profile->ops->create_pin(profile, p15card, df, pin_obj, args->so_pin, args->so_pin_len, args->so_puk, args->so_puk_len); if (pin_obj) /* Remove 'virtual' AUTH object . */ sc_pkcs15_remove_object(p15card, pin_obj); 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) { 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) { app->aid.len = p15card->file_app->namelen; memcpy(app->aid.value, p15card->file_app->name, app->aid.len); } /* set serial number if explicitly specified */ if (args->serial) { sc_pkcs15init_set_serial(profile, args->serial); } else { /* otherwise try to get the serial number from the card */ struct sc_serial_number serialnr; r = sc_card_ctl(card, SC_CARDCTL_GET_SERIALNR, &serialnr); if (r == SC_SUCCESS) { char hex_serial[SC_MAX_SERIALNR * 2 + 1]; sc_bin_to_hex(serialnr.value, serialnr.len, hex_serial, sizeof(hex_serial), 0); sc_pkcs15init_set_serial(profile, hex_serial); } } if (args->label) { free(p15card->tokeninfo->label); p15card->tokeninfo->label = strdup(args->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) { r = sc_pkcs15init_update_tokeninfo(p15card, profile); } 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); } /* * Store a PIN/PUK pair */ static int sc_pkcs15init_store_puk(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15init_pinargs *args) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_object *pin_obj; struct sc_pkcs15_auth_info *auth_info; int r; char puk_label[0x30]; LOG_FUNC_CALLED(ctx); if (!args->puk_id.len) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "PUK auth ID not supplied"); /* Make sure we don't get duplicate PIN IDs */ r = sc_pkcs15_find_pin_by_auth_id(p15card, &args->puk_id, NULL); if (r != SC_ERROR_OBJECT_NOT_FOUND) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "There already is a PIN with this ID."); if (!args->puk_label) { if (args->label) snprintf(puk_label, sizeof(puk_label), "%s (PUK)", args->label); else snprintf(puk_label, sizeof(puk_label), "User PUK"); args->puk_label = puk_label; } args->pin = args->puk; args->pin_len = args->puk_len; args->puk = NULL; args->puk_len = 0; pin_obj = sc_pkcs15init_new_object(SC_PKCS15_TYPE_AUTH_PIN, args->puk_label, NULL, NULL); if (pin_obj == NULL) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot allocate PIN object"); 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) { r = sc_pkcs15init_create_pin(p15card, profile, pin_obj, args); LOG_TEST_GOTO_ERR(ctx, r, "Failed to create PIN"); } else { r = SC_ERROR_NOT_SUPPORTED; LOG_TEST_GOTO_ERR(ctx, r, "In Old API store PUK object is not supported"); } 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); } int sc_pkcs15init_store_pin(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15init_pinargs *args) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_object *pin_obj; struct sc_pkcs15_auth_info *auth_info; int r; LOG_FUNC_CALLED(ctx); /* No auth_id given: select one */ if (args->auth_id.len == 0) { unsigned int n; args->auth_id.len = 1; for (n = 1, r = 0; n < 256; n++) { args->auth_id.value[0] = n; r = sc_pkcs15_find_pin_by_auth_id(p15card, &args->auth_id, NULL); if (r == SC_ERROR_OBJECT_NOT_FOUND) break; } if (r != SC_ERROR_OBJECT_NOT_FOUND) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "No auth_id specified for new PIN"); } else { /* Make sure we don't get duplicate PIN IDs */ r = sc_pkcs15_find_pin_by_auth_id(p15card, &args->auth_id, NULL); if (r != SC_ERROR_OBJECT_NOT_FOUND) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "There already is a PIN with this ID."); } pin_obj = sc_pkcs15init_new_object(SC_PKCS15_TYPE_AUTH_PIN, args->label, NULL, NULL); if (pin_obj == NULL) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot allocate PIN object"); 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)); 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); 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); } static int sc_pkcs15init_create_pin(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15_object *pin_obj, struct sc_pkcs15init_pinargs *args) { 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_file *df = profile->df_info->file; int r, retry = 0; LOG_FUNC_CALLED(ctx); /* Some cards need to keep all their PINs in separate directories. * Create a subdirectory now, and put the pin into * this subdirectory */ if (profile->pin_domains) { if (!profile->ops->create_domain) LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "PIN domains not supported."); r = profile->ops->create_domain(profile, p15card, &auth_info->auth_id, &df); LOG_TEST_RET(ctx, r, "Card specific create domain failed"); } /* Path encoded only for local PINs */ if (pin_attrs->flags & SC_PKCS15_PIN_FLAG_LOCAL) auth_info->path = df->path; /* pin_info->reference = 0; */ /* Loop until we come up with an acceptable pin reference */ while (1) { if (profile->ops->select_pin_reference) { r = profile->ops->select_pin_reference(profile, p15card, auth_info); LOG_TEST_RET(ctx, r, "Card specific select PIN reference failed"); retry = 1; } r = sc_pkcs15_find_pin_by_reference(p15card, &auth_info->path, pin_attrs->reference, NULL); if (r == SC_ERROR_OBJECT_NOT_FOUND) break; if (r != 0 || !retry) /* Other error trying to retrieve pin obj */ LOG_TEST_RET(ctx, SC_ERROR_TOO_MANY_OBJECTS, "Failed to allocate PIN reference."); pin_attrs->reference++; } if (args->puk_len == 0) pin_attrs->flags |= SC_PKCS15_PIN_FLAG_UNBLOCK_DISABLED; sc_log(ctx, "create PIN with reference:%X, flags:%X, path:%s", pin_attrs->reference, pin_attrs->flags, sc_print_path(&auth_info->path)); r = profile->ops->create_pin(profile, p15card, df, pin_obj, args->pin, args->pin_len, args->puk, args->puk_len); if (df != profile->df_info->file) sc_file_free(df); LOG_FUNC_RETURN(ctx, r); } /* * Default function for creating a pin subdirectory */ int sc_pkcs15_create_pin_domain(struct sc_profile *profile, struct sc_pkcs15_card *p15card, const struct sc_pkcs15_id *id, struct sc_file **ret) { struct sc_context *ctx = p15card->card->ctx; struct sc_file *df = profile->df_info->file; int r; sc_log(ctx, "create PIN domain (path:%s,ID:%s)", sc_print_path(&df->path), sc_pkcs15_print_id(id)); /* Instantiate PIN directory just below the application DF */ r = sc_profile_instantiate_template(profile, "pin-domain", &df->path, "pin-dir", id, ret); if (r >= 0) { sc_log(ctx, "create PIN DF(path:%s)", sc_print_path(&(*ret)->path)); r = profile->ops->create_dir(profile, p15card, *ret); } return r; } static int sc_pkcs15init_encode_prvkey_content(struct sc_pkcs15_card *p15card, struct sc_pkcs15_prkey *prvkey, struct sc_pkcs15_object *object) { struct sc_context *ctx = p15card->card->ctx; LOG_FUNC_CALLED(ctx); if (prvkey->algorithm == SC_ALGORITHM_RSA) { struct sc_pkcs15_pubkey pubkey; int rv; pubkey.algorithm = prvkey->algorithm; pubkey.u.rsa.modulus = prvkey->u.rsa.modulus; pubkey.u.rsa.exponent = prvkey->u.rsa.exponent; rv = sc_pkcs15_encode_pubkey(ctx, &pubkey, &object->content.value, &object->content.len); LOG_TEST_RET(ctx, rv, "Failed to encode public key"); } LOG_FUNC_RETURN(ctx, SC_SUCCESS); } /* * Prepare private key download, and initialize a prkdf entry */ static int sc_pkcs15init_init_prkdf(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15init_prkeyargs *keyargs, struct sc_pkcs15_prkey *key, int keybits, struct sc_pkcs15_object **res_obj) { struct sc_context *ctx = p15card->card->ctx; 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) { r = SC_ERROR_INVALID_ARGUMENTS; LOG_TEST_GOTO_ERR(ctx, r, "Initialize PrKDF entry failed"); } *res_obj = NULL; if ((usage = keyargs->usage) == 0) { usage = SC_PKCS15_PRKEY_USAGE_SIGN; if (keyargs->x509_usage) usage = sc_pkcs15init_map_usage(keyargs->x509_usage, 1); } if ((label = keyargs->label) == NULL) label = DEFAULT_PRIVATE_KEY_LABEL; /* Create the prkey object now. * If we find out below that we're better off reusing an * existing object, we'll ditch this one */ key_type = key_pkcs15_algo(p15card, key->algorithm); r = key_type; LOG_TEST_GOTO_ERR(ctx, r, "Unsupported key type"); object = sc_pkcs15init_new_object(key_type, label, &keyargs->auth_id, NULL); 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; key_info->native = 1; 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; } /* 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_PRKEY, &keyargs->id); LOG_TEST_GOTO_ERR(ctx, r, "Cannot select ID for PrKey object"); key_info->id = keyargs->id; 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) */ key_info->params.data = malloc(key_info->params.len); if (!key_info->params.data) { r = SC_ERROR_OUT_OF_MEMORY; LOG_TEST_GOTO_ERR(ctx, r, "Cannot allocate memory for GOST parameters"); } keyinfo_gostparams = key_info->params.data; 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) { /* 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; 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"); } /* copy ecparams into allocated one * it will be freed with the corresponding object */ memcpy(new_ecparams, ecparams, sizeof(struct sc_ec_parameters)); new_ecparams->named_curve = strdup(ecparams->named_curve); new_ecparams->der.value = malloc(ecparams->der.len); if (!new_ecparams->named_curve || !new_ecparams->der.value) { r = SC_ERROR_OUT_OF_MEMORY; LOG_TEST_GOTO_ERR(ctx, r, "Cannot allocate memory for EC parameters"); } memcpy(new_ecparams->der.value, ecparams->der.value, ecparams->der.len); 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; } r = select_object_path(p15card, profile, object, &key_info->path); LOG_TEST_GOTO_ERR(ctx, r, "Failed to select private key object path"); /* See if we need to select a key reference for this object */ if (profile->ops->select_key_reference) { while (1) { sc_log(ctx, "Look for usable key reference starting from %i", key_info->key_reference); r = profile->ops->select_key_reference(profile, p15card, key_info); LOG_TEST_GOTO_ERR(ctx, r, "Failed to select card specific key reference"); r = sc_pkcs15_find_prkey_by_reference(p15card, &key_info->path, key_info->key_reference, NULL); if (r == SC_ERROR_OBJECT_NOT_FOUND) { sc_log(ctx, "Will use key reference %i", key_info->key_reference); break; } if (r != 0) { /* Other error trying to retrieve pin obj */ r = SC_ERROR_TOO_MANY_OBJECTS; LOG_TEST_GOTO_ERR(ctx, r, "Failed to select key reference"); } key_info->key_reference++; } } *res_obj = object; object = NULL; new_ecparams = NULL; r = SC_SUCCESS; err: if (new_ecparams) { free(new_ecparams->named_curve); free(new_ecparams->der.value); free(new_ecparams); key_info->params.data = NULL; } sc_pkcs15init_free_object(object); LOG_FUNC_RETURN(ctx, r); } /* * Prepare secret key download, and initialize a skdf entry */ static int sc_pkcs15init_init_skdf(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15init_skeyargs *keyargs, struct sc_pkcs15_object **res_obj) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_skey_info *key_info; struct sc_pkcs15_object *object = NULL; const char *label; unsigned int usage; unsigned long keybits = keyargs->value_len; int r = 0, key_type; LOG_FUNC_CALLED(ctx); if (!res_obj || !keybits) { r = SC_ERROR_INVALID_ARGUMENTS; LOG_TEST_GOTO_ERR(ctx, r, "Initialize SKDF entry failed"); } *res_obj = NULL; if ((usage = keyargs->usage) == 0) { usage = SC_PKCS15_PRKEY_USAGE_ENCRYPT|SC_PKCS15_PRKEY_USAGE_DECRYPT; } if ((label = keyargs->label) == NULL) label = DEFAULT_SECRET_KEY_LABEL; /* Create the skey object now. * If we find out below that we're better off reusing an * existing object, we'll ditch this one */ key_type = key_pkcs15_algo(p15card, keyargs->algorithm); r = key_type; LOG_TEST_GOTO_ERR(ctx, r, "Unsupported key type"); object = sc_pkcs15init_new_object(key_type, label, &keyargs->auth_id, NULL); 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; key_info->native = 1; key_info->key_reference = 0; switch (keyargs->algorithm) { case SC_ALGORITHM_DES: key_info->key_type = CKK_DES; break; case SC_ALGORITHM_3DES: key_info->key_type = CKK_DES3; break; case SC_ALGORITHM_AES: key_info->key_type = CKK_AES; break; default: key_info->key_type = CKK_GENERIC_SECRET; break; } key_info->value_len = keybits; key_info->access_flags = keyargs->access_flags; /* Path is selected below */ if (keyargs->access_flags & SC_PKCS15_PRKEY_ACCESS_EXTRACTABLE) { key_info->access_flags &= ~SC_PKCS15_PRKEY_ACCESS_NEVEREXTRACTABLE; } 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); LOG_TEST_GOTO_ERR(ctx, r, "Cannot select ID for SKey object"); key_info->id = keyargs->id; r = select_object_path(p15card, profile, object, &key_info->path); 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) { 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: sc_pkcs15init_free_object(object); LOG_FUNC_RETURN(ctx, r); } static int _pkcd15init_set_aux_md_data(struct sc_pkcs15_card *p15card, struct sc_auxiliary_data **aux_data, unsigned char *guid, size_t guid_len) { struct sc_context *ctx = p15card->card->ctx; unsigned char flags = SC_MD_CONTAINER_MAP_VALID_CONTAINER; char gd[SC_MD_MAX_CONTAINER_NAME_LEN + 1]; int rv; LOG_FUNC_CALLED(ctx); if(!guid || !guid_len) LOG_FUNC_RETURN(ctx, SC_SUCCESS); if (!aux_data) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); if (guid_len > SC_MD_MAX_CONTAINER_NAME_LEN) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); memset(gd, 0, sizeof(gd)); memcpy(gd, guid, guid_len); if (*aux_data == NULL) { rv = sc_aux_data_allocate(ctx, aux_data, NULL); LOG_TEST_RET(ctx, rv, "Failed to allocate aux data"); } rv = sc_aux_data_set_md_guid(ctx, *aux_data, gd); LOG_TEST_RET(ctx, rv, "Failed to set private key CMAP record GUID"); if (sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_PRKEY, NULL, 0) == 0) flags |= SC_MD_CONTAINER_MAP_DEFAULT_CONTAINER; rv = sc_aux_data_set_md_flags(ctx, *aux_data, flags); LOG_TEST_RET(ctx, rv, "Failed to set private key CMAP record flags"); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } /* * Copy gost3410 parameters (e.g. from prkey to pubkey) */ static int sc_copy_gost_params(struct sc_pkcs15_gost_parameters *dst, struct sc_pkcs15_gost_parameters *src) { if (!dst || !src) return SC_ERROR_INVALID_ARGUMENTS; memcpy((dst->key).value, (src->key).value, sizeof((src->key).value)); memcpy((dst->hash).value, (src->hash).value, sizeof((src->hash).value)); memcpy((dst->cipher).value, (src->cipher).value, sizeof((src->cipher).value)); return SC_SUCCESS; } /* * Generate a new private key */ int sc_pkcs15init_generate_key(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15init_keygen_args *keygen_args, unsigned int keybits, struct sc_pkcs15_object **res_obj) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15init_pubkeyargs pubkey_args; struct sc_pkcs15_object *object = NULL; 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, algorithm, &keygen_args->prkey_args, &keybits); LOG_TEST_RET(ctx, r, "Invalid key size"); if (check_key_compatibility(p15card, algorithm, &keygen_args->prkey_args.key, keygen_args->prkey_args.x509_usage, 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) { 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) { 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_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_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. */ 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 (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_GOTO_ERR(ctx, r, "Cannot allocate GOST parameters"); } else if (algorithm == SC_ALGORITHM_EC) { /* needs to be freed in case of failure when pubkey is not set yet */ r = sc_copy_ec_params(&pubkey_args.key.u.ec.params, &keygen_args->prkey_args.key.u.ec.params); 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_GOTO_ERR(ctx, r, "Cannot generate key: create key failed"); r = profile->ops->generate_key(profile, p15card, object, &pubkey_args.key); LOG_TEST_GOTO_ERR(ctx, r, "Failed to generate key"); /* update PrKDF entry */ if (!caller_supplied_id) { struct sc_pkcs15_id iid; /* Caller not supplied ID, so, * 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_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) { 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; } pubkey_args.id = key_info->id; r = sc_pkcs15_encode_pubkey(ctx, pubkey, &object->content.value, &object->content.len); LOG_TEST_GOTO_ERR(ctx, r, "Failed to encode public key"); r = sc_pkcs15init_add_object(p15card, profile, SC_PKCS15_PRKDF, 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; 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); 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; object = NULL; profile->dirty = 1; err: sc_pkcs15_free_object(object); sc_pkcs15_erase_pubkey(&pubkey_args.key); if (algorithm == SC_ALGORITHM_EC) { /* allocated in check_keygen_params_consistency() */ free(keygen_args->prkey_args.key.u.ec.params.der.value); keygen_args->prkey_args.key.u.ec.params.der.value = NULL; } LOG_FUNC_RETURN(ctx, r); } /* * Generate a new secret key */ int sc_pkcs15init_generate_secret_key(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15init_skeyargs *skey_args, struct sc_pkcs15_object **res_obj) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_object *object = NULL; unsigned int keybits = (unsigned int)skey_args->value_len; int r; LOG_FUNC_CALLED(ctx); /* check supported key size */ r = check_keygen_params_consistency(p15card->card, skey_args->algorithm, NULL, &keybits); LOG_TEST_RET(ctx, r, "Invalid key size"); if (check_key_compatibility(p15card, skey_args->algorithm, NULL, 0, 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) LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Key generation not supported"); if (skey_args->id.len) { /* Make sure that secret key's ID is the unique inside the PKCS#15 application */ r = sc_pkcs15_find_skey_by_id(p15card, &skey_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"); } /* Set up the SKDF object */ r = sc_pkcs15init_init_skdf(p15card, profile, skey_args, &object); 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_GOTO_ERR(ctx, r, "Cannot generate key: create key failed"); r = profile->ops->generate_key(profile, p15card, object, NULL); LOG_TEST_GOTO_ERR(ctx, r, "Failed to generate key"); r = sc_pkcs15init_add_object(p15card, profile, SC_PKCS15_SKDF, 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_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); } /* * Store private key */ int sc_pkcs15init_store_private_key(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15init_prkeyargs *keyargs, struct sc_pkcs15_object **res_obj) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_object *object = NULL; struct sc_pkcs15_prkey key; struct sc_pkcs15_prkey_info *key_info = NULL; 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; r = prkey_fixup(p15card, &key); LOG_TEST_RET(ctx, r, "Private key data sanity check failed"); 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 * 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 for crypto. Cannot store it as non extractable."); } /* Select a intrinsic Key ID if user didn't specify one */ r = sc_pkcs15init_select_intrinsic_id(p15card, profile, SC_PKCS15_TYPE_PRKEY, &keyargs->id, &keyargs->key); LOG_TEST_RET(ctx, r, "Get intrinsic ID error"); /* Make sure that private key's ID is the unique inside the PKCS#15 application */ r = sc_pkcs15_find_prkey_by_id(p15card, &keyargs->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"); /* Set up the PrKDF object */ r = sc_pkcs15init_init_prkdf(p15card, profile, keyargs, &key, keybits, &object); LOG_TEST_RET(ctx, r, "Failed to initialize private key object"); r = sc_pkcs15init_encode_prvkey_content(p15card, &key, object); 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_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_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_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_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_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_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 public key */ int sc_pkcs15init_store_public_key(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15init_pubkeyargs *keyargs, struct sc_pkcs15_object **res_obj) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_object *object = NULL; struct sc_pkcs15_pubkey_info *key_info; struct sc_pkcs15_keyinfo_gostparams *keyinfo_gostparams; struct sc_pkcs15_pubkey key; struct sc_path *path; const char *label; 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 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; 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; 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_GOTO_ERR(ctx, r, "Failed to fix EC public key parameters"); keybits = key.u.ec.params.field_length; break; default: 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) usage = sc_pkcs15init_map_usage(keyargs->x509_usage, 0); } 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) { 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; 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) */ key_info->params.data = malloc(key_info->params.len); if (!key_info->params.data) { r = SC_ERROR_OUT_OF_MEMORY; LOG_TEST_GOTO_ERR(ctx, r, "Cannot allocate GOST params"); } keyinfo_gostparams = key_info->params.data; 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) { key_info->field_length = keybits; if (key.u.ec.params.der.value) { key_info->params.data = malloc(key.u.ec.params.der.len); if (!key_info->params.data) { r = SC_ERROR_OUT_OF_MEMORY; LOG_TEST_GOTO_ERR(ctx, r, "Cannot allocate EC params"); } 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 params"); } 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 */ r = sc_pkcs15init_select_intrinsic_id(p15card, profile, SC_PKCS15_TYPE_PUBKEY, &keyargs->id, &key); LOG_TEST_GOTO_ERR(ctx, r, "Get intrinsic ID error"); /* Select a Key ID if the user didn't specify one and there is no intrinsic ID, * otherwise make sure it's unique */ r = select_id(p15card, SC_PKCS15_TYPE_PUBKEY, &keyargs->id); LOG_TEST_GOTO_ERR(ctx, r, "Failed to select public key object ID"); /* Make sure that private key's ID is the unique inside the PKCS#15 application */ r = sc_pkcs15_find_pubkey_by_id(p15card, &keyargs->id, NULL); if (!r) { r = SC_ERROR_NON_UNIQUE_ID; LOG_TEST_GOTO_ERR(ctx, r, "Non unique ID of the public key object"); } else if (r != SC_ERROR_OBJECT_NOT_FOUND) { LOG_TEST_GOTO_ERR(ctx, r, "Find public key error"); } key_info->id = keyargs->id; /* DER encode public key components */ r = sc_pkcs15_encode_pubkey(p15card->card->ctx, &key, &object->content.value, &object->content.len); LOG_TEST_GOTO_ERR(ctx, r, "Encode public key error"); r = sc_pkcs15_encode_pubkey(p15card->card->ctx, &key, &key_info->direct.raw.value, &key_info->direct.raw.len); LOG_TEST_GOTO_ERR(ctx, r, "RAW encode public key error"); /* EC key are encoded as SPKI to preserve domain parameter */ r = sc_pkcs15_encode_pubkey_as_spki(p15card->card->ctx, &key, &key_info->direct.spki.value, &key_info->direct.spki.len); 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) 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); path = &key_info->path; if (path->count == 0) { path->index = 0; path->count = -1; } /* 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: sc_pkcs15_erase_pubkey(&key); sc_pkcs15_free_object(object); LOG_FUNC_RETURN(ctx, r); } /* * Store secret key */ int sc_pkcs15init_store_secret_key(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15init_skeyargs *keyargs, struct sc_pkcs15_object **res_obj) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_object *object = NULL; int r = 0; 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->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 for crypto. Cannot store it as non extractable."); } #ifdef ENABLE_OPENSSL if (!keyargs->id.len) { /* Calculating intrinsic Key ID for secret key does not make * sense - just generate random one */ if (RAND_bytes(keyargs->id.value, 20) == 1) keyargs->id.len = 20; } #endif /* Make sure that secret key's ID is the unique inside the PKCS#15 application */ r = sc_pkcs15_find_skey_by_id(p15card, &keyargs->id, NULL); if (!r) LOG_TEST_RET(ctx, SC_ERROR_NON_UNIQUE_ID, "Non unique ID of the secret key object"); else if (r != SC_ERROR_OBJECT_NOT_FOUND) LOG_TEST_RET(ctx, r, "Find secret key error"); /* Set up the SKDF object */ r = sc_pkcs15init_init_skdf(p15card, profile, keyargs, &object); LOG_TEST_RET(ctx, r, "Failed to initialize secret key object"); if (profile->ops->create_key) r = profile->ops->create_key(profile, p15card, object); 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_GOTO_ERR(ctx, r, "Card specific 'store key' failed"); sc_pkcs15_free_object_content(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 && !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_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 */ int sc_pkcs15init_store_certificate(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15init_certargs *args, struct sc_pkcs15_object **res_obj) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_cert_info *cert_info = NULL; struct sc_pkcs15_object *object = NULL; struct sc_pkcs15_object *key_object = NULL; struct sc_path existing_path; const char *label = NULL; int r; LOG_FUNC_CALLED(ctx); memset(&existing_path, 0, sizeof(struct sc_path)); label = args->label; if (!label) label = "Certificate"; r = sc_pkcs15init_select_intrinsic_id(p15card, profile, SC_PKCS15_TYPE_CERT_X509, &args->id, &args->der_encoded); LOG_TEST_RET(ctx, r, "Get certificate 'intrinsic ID' error"); sc_log(ctx, "Cert(ID:%s) rv %i", sc_pkcs15_print_id(&args->id), r); /* Select an ID if the user didn't specify one, otherwise make sure it's unique */ r = select_id(p15card, SC_PKCS15_TYPE_CERT, &args->id); if (r == SC_ERROR_NON_UNIQUE_ID && args->update) { struct sc_pkcs15_object *existing_obj = NULL; r = sc_pkcs15_find_object_by_id(p15card, SC_PKCS15_TYPE_CERT, &args->id, &existing_obj); if (!r) { sc_log(ctx, "Found cert(ID:%s)", sc_pkcs15_print_id(&args->id)); existing_path = ((struct sc_pkcs15_cert_info *)existing_obj->data)->path; sc_pkcs15_remove_object(p15card, existing_obj); sc_pkcs15_free_object(existing_obj); } r = select_id(p15card, SC_PKCS15_TYPE_CERT, &args->id); } sc_log(ctx, "Select ID Cert(ID:%s) rv %i", sc_pkcs15_print_id(&args->id), r); LOG_TEST_RET(ctx, r, "Select certificate ID error"); object = sc_pkcs15init_new_object(SC_PKCS15_TYPE_CERT_X509, label, NULL, NULL); if (object == NULL) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Failed to allocate certificate object"); cert_info = (struct sc_pkcs15_cert_info *) object->data; cert_info->id = args->id; cert_info->authority = args->authority; sc_der_copy(&object->content, &args->der_encoded); sc_der_copy(&cert_info->value, &args->der_encoded); if (existing_path.len) { sc_log(ctx, "Using existing path %s", sc_print_path(&existing_path)); cert_info->path = existing_path; } sc_log(ctx, "Store cert(%.*s,ID:%s,der(%p,%"SC_FORMAT_LEN_SIZE_T"u))", (int) sizeof object->label, object->label, sc_pkcs15_print_id(&cert_info->id), args->der_encoded.value, args->der_encoded.len); if (!profile->pkcs15.direct_certificates) r = sc_pkcs15init_store_data(p15card, profile, object, &args->der_encoded, &cert_info->path); /* Now update the CDF */ if (r >= 0) { r = sc_pkcs15init_add_object(p15card, profile, SC_PKCS15_CDF, object); /* TODO: update private key PKCS#15 object with the certificate's attributes */ } if (r >= 0) { r = sc_pkcs15_prkey_attrs_from_cert(p15card, object, &key_object); if (r) { r = 0; } else if (key_object) { if (profile->ops->emu_update_any_df) { r = profile->ops->emu_update_any_df(profile, p15card, SC_AC_OP_UPDATE, key_object); if (r == SC_ERROR_NOT_SUPPORTED) r = SC_SUCCESS; } else { r = sc_pkcs15init_update_any_df(p15card, profile, key_object->df, 0); sc_log(ctx, "update_any_df returned %i", r); } } } if (r < 0) { sc_pkcs15_remove_object(p15card, object); sc_pkcs15_free_object(object); } else if (res_obj) { *res_obj = object; } profile->dirty = 1; LOG_FUNC_RETURN(ctx, r); } /* * Store a data object */ int sc_pkcs15init_store_data_object(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15init_dataargs *args, struct sc_pkcs15_object **res_obj) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_data_info *data_object_info; struct sc_pkcs15_object *object; struct sc_pkcs15_object *objs[32]; const char *label; int r, i; unsigned int tid = 0x01; LOG_FUNC_CALLED(ctx); if (!profile) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Missing profile"); label = args->label; if (!args->id.len) { /* Select an ID if the user didn't specify one, otherwise * make sure it's unique (even though data objects doesn't * have a pkcs15 id we need one here to create a unique * file id from the data file template */ r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_DATA_OBJECT, objs, 32); LOG_TEST_RET(ctx, r, "Get 'DATA' objects error"); for (i = 0; i < r; i++) { unsigned char cid; struct sc_pkcs15_data_info *cinfo = (struct sc_pkcs15_data_info *) objs[i]->data; if (!cinfo->path.len) continue; cid = cinfo->path.value[cinfo->path.len - 1]; if (cid >= tid) tid = cid + 1; } if (tid > 0xff) /* too many data objects ... */ return SC_ERROR_TOO_MANY_OBJECTS; args->id.len = 1; args->id.value[0] = tid; } else { /* in case the user specifies an id it should be at most * one byte long */ if (args->id.len > 1) return SC_ERROR_INVALID_ARGUMENTS; } object = sc_pkcs15init_new_object(SC_PKCS15_TYPE_DATA_OBJECT, label, &args->auth_id, NULL); if (object == NULL) return SC_ERROR_OUT_OF_MEMORY; data_object_info = (struct sc_pkcs15_data_info *) object->data; if (args->app_label != NULL) strlcpy(data_object_info->app_label, args->app_label, sizeof(data_object_info->app_label)); else if (label != NULL) strlcpy(data_object_info->app_label, label, sizeof(data_object_info->app_label)); data_object_info->app_oid = args->app_oid; 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_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_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); } int sc_pkcs15init_get_pin_reference(struct sc_pkcs15_card *p15card, struct sc_profile *profile, unsigned auth_method, int reference) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_auth_info auth_info; struct sc_pkcs15_object *auth_objs[0x10]; int r, ii, nn_objs; LOG_FUNC_CALLED(ctx); /* 1. Look for the corresponding pkcs15 PIN object. */ /* Get all existing pkcs15 AUTH objects */ r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_AUTH_PIN, auth_objs, 0x10); LOG_TEST_RET(ctx, r, "Get PKCS#15 AUTH objects error"); nn_objs = r; sc_log(ctx, "found %i auth objects; looking for AUTH object(auth_method:%i,reference:%i)", nn_objs, auth_method, reference); for (ii=0; iidata; struct sc_pkcs15_pin_attributes *pin_attrs = &auth_info->attrs.pin; sc_log(ctx, "check PIN(%.*s,auth_method:%i,type:%i,reference:%i,flags:%X)", (int) sizeof auth_objs[ii]->label, auth_objs[ii]->label, auth_info->auth_method, pin_attrs->type, pin_attrs->reference, pin_attrs->flags); /* Find out if there is AUTH pkcs15 object with given 'type' and 'reference' */ if (auth_info->auth_method == auth_method && pin_attrs->reference == reference) LOG_FUNC_RETURN(ctx, pin_attrs->reference); if (auth_method != SC_AC_SYMBOLIC) continue; /* Translate 'SYMBOLIC' PIN reference into the pkcs#15 pinAttributes.flags * and check for the existing pkcs15 PIN object with these flags. */ switch (reference) { case SC_PKCS15INIT_USER_PIN: if (pin_attrs->flags & SC_PKCS15_PIN_FLAG_SO_PIN) continue; if (pin_attrs->flags & SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN) continue; break; case SC_PKCS15INIT_SO_PIN: if (pin_attrs->flags & SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN) continue; if (!(pin_attrs->flags & SC_PKCS15_PIN_FLAG_SO_PIN)) continue; break; case SC_PKCS15INIT_USER_PUK: if (pin_attrs->flags & SC_PKCS15_PIN_FLAG_SO_PIN) continue; if (!(pin_attrs->flags & SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN)) continue; break; case SC_PKCS15INIT_SO_PUK: if (!(pin_attrs->flags & SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN)) continue; if (!(pin_attrs->flags & SC_PKCS15_PIN_FLAG_SO_PIN)) continue; break; default: LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Invalid Symbolic PIN reference"); } LOG_FUNC_RETURN(ctx, pin_attrs->reference); } /* 2. No existing pkcs15 PIN object * -- check if profile defines some PIN with 'reference' as PIN reference. */ r = sc_profile_get_pin_id_by_reference(profile, auth_method, reference, &auth_info); if (r < 0) LOG_TEST_RET(ctx, SC_ERROR_OBJECT_NOT_FOUND, "PIN template not found"); LOG_FUNC_RETURN(ctx, auth_info.attrs.pin.reference); } static int sc_pkcs15init_store_data(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15_object *object, struct sc_pkcs15_der *data, struct sc_path *path) { struct sc_context *ctx = p15card->card->ctx; struct sc_file *file = NULL; int r; LOG_FUNC_CALLED(ctx); if (profile->ops->emu_store_data) { r = profile->ops->emu_store_data(p15card, profile, object, data, path); if (r == SC_SUCCESS || r != SC_ERROR_NOT_IMPLEMENTED) LOG_FUNC_RETURN(ctx, r); } r = select_object_path(p15card, profile, object, path); LOG_TEST_RET(ctx, r, "Failed to select object path"); r = sc_profile_get_file_by_path(profile, path, &file); LOG_TEST_RET(ctx, r, "Failed to get file by path"); if (file->path.count == 0) { file->path.index = 0; file->path.count = -1; } r = sc_pkcs15init_delete_by_path(profile, p15card, &file->path); if (r && r != SC_ERROR_FILE_NOT_FOUND) { sc_file_free(file); LOG_TEST_RET(ctx, r, "Cannot delete file"); } r = sc_pkcs15init_update_file(profile, p15card, file, data->value, data->len); *path = file->path; sc_file_free(file); LOG_FUNC_RETURN(ctx, r); } /* * Map X509 keyUsage extension bits to PKCS#15 keyUsage bits */ typedef struct { unsigned long x509_usage; unsigned int p15_usage; } sc_usage_map; static sc_usage_map x509_to_pkcs15_private_key_usage[16] = { { SC_PKCS15INIT_X509_DIGITAL_SIGNATURE, SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_SIGNRECOVER }, { SC_PKCS15INIT_X509_NON_REPUDIATION, SC_PKCS15_PRKEY_USAGE_NONREPUDIATION }, { SC_PKCS15INIT_X509_KEY_ENCIPHERMENT, SC_PKCS15_PRKEY_USAGE_UNWRAP }, { SC_PKCS15INIT_X509_DATA_ENCIPHERMENT, SC_PKCS15_PRKEY_USAGE_DECRYPT }, { SC_PKCS15INIT_X509_KEY_AGREEMENT, SC_PKCS15_PRKEY_USAGE_DERIVE }, { SC_PKCS15INIT_X509_KEY_CERT_SIGN, SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_SIGNRECOVER }, { SC_PKCS15INIT_X509_CRL_SIGN, SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_SIGNRECOVER } }; static sc_usage_map x509_to_pkcs15_public_key_usage[16] = { { SC_PKCS15INIT_X509_DIGITAL_SIGNATURE, SC_PKCS15_PRKEY_USAGE_VERIFY | SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER }, { SC_PKCS15INIT_X509_NON_REPUDIATION, SC_PKCS15_PRKEY_USAGE_NONREPUDIATION }, { SC_PKCS15INIT_X509_KEY_ENCIPHERMENT, SC_PKCS15_PRKEY_USAGE_WRAP }, { SC_PKCS15INIT_X509_DATA_ENCIPHERMENT, SC_PKCS15_PRKEY_USAGE_ENCRYPT }, { SC_PKCS15INIT_X509_KEY_AGREEMENT, SC_PKCS15_PRKEY_USAGE_DERIVE }, { SC_PKCS15INIT_X509_KEY_CERT_SIGN, SC_PKCS15_PRKEY_USAGE_VERIFY | SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER }, { SC_PKCS15INIT_X509_CRL_SIGN, SC_PKCS15_PRKEY_USAGE_VERIFY | SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER } }; static unsigned int sc_pkcs15init_map_usage(unsigned long x509_usage, int _private) { unsigned int p15_usage = 0, n; sc_usage_map *map; map = _private ? x509_to_pkcs15_private_key_usage : x509_to_pkcs15_public_key_usage; for (n = 0; n < 16; n++) { if (x509_usage & map[n].x509_usage) p15_usage |= map[n].p15_usage; } return p15_usage; } /* * Compute modulus length */ static size_t sc_pkcs15init_keybits(struct sc_pkcs15_bignum *bn) { unsigned int mask; size_t bits; if (!bn || !bn->len) return 0; bits = bn->len << 3; for (mask = 0x80; mask && !(bn->data[0] & mask); mask >>= 1) bits--; return bits; } /* * Check consistency of the key parameters. */ static int check_keygen_params_consistency(struct sc_card *card, 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) { struct sc_ec_parameters *ecparams = &prkey->key.u.ec.params; rv = sc_pkcs15_fix_ec_parameters(ctx, ecparams); LOG_TEST_RET(ctx, rv, "Cannot fix EC parameters"); sc_log(ctx, "EC parameters: %s", sc_dump_hex(ecparams->der.value, ecparams->der.len)); if (!*keybits) *keybits = (unsigned int)ecparams->field_length; } for (i = 0; i < card->algorithm_count; i++) { struct sc_algorithm_info *info = &card->algorithms[i]; if (info->algorithm != alg) continue; if (info->key_length != *keybits) continue; LOG_FUNC_RETURN(ctx, SC_SUCCESS); } if (alg == SC_ALGORITHM_EC && prkey) /* allocated in sc_pkcs15_fix_ec_parameters */ free(prkey->key.u.ec.params.der.value); 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 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; LOG_FUNC_CALLED(ctx); 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 (flags != 0 && ((info->flags & flags) != flags)) continue; if (alg == SC_ALGORITHM_RSA && prkey) { if (info->u._rsa.exponent != 0 && prkey->u.rsa.exponent.len != 0) { struct sc_pkcs15_bignum *e = &prkey->u.rsa.exponent; unsigned long exponent = 0; unsigned int n; if (e->len > 4) continue; for (n = 0; n < e->len; n++) { exponent <<= 8; exponent |= e->data[n]; } if (info->u._rsa.exponent != exponent) continue; } } else if (alg == SC_ALGORITHM_EC) { 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); if (sc_valid_oid(&info->u._ec.params.id)) if (!sc_compare_oid(&info->u._ec.params.id, &prkey->u.ec.params.id)) continue; } LOG_FUNC_RETURN(ctx, SC_SUCCESS); } LOG_FUNC_RETURN(ctx, SC_ERROR_OBJECT_NOT_VALID); } /* * Check RSA key for consistency, and compute missing * CRT elements */ 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"); return SC_ERROR_INVALID_ARGUMENTS; } #ifdef ENABLE_OPENSSL /* Generate additional parameters. * At least the GPK seems to need the full set of CRT * parameters; storing just the private exponent produces * invalid signatures. * The cryptoflex does not seem to be able to do any sort * of RSA without the full set of CRT coefficients either */ /* We don't really need an RSA structure, only the BIGNUMs */ if (!key->dmp1.len || !key->dmq1.len || !key->iqmp.len) { 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(); if (!aux || !bn_ctx) { sc_log_openssl(ctx); r = SC_ERROR_INTERNAL; goto end; } 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) { key->dmp1.len = BN_num_bytes(rsa_dmp1); key->dmp1.data = malloc(key->dmp1.len); if (key->dmp1.data) { BN_bn2bin(rsa_dmp1, key->dmp1.data); } else { key->dmp1.len = 0; } } if (key->dmq1.data == NULL) { key->dmq1.len = BN_num_bytes(rsa_dmq1); key->dmq1.data = malloc(key->dmq1.len); if (key->dmq1.data) { BN_bn2bin(rsa_dmq1, key->dmq1.data); } else { key->dmq1.len = 0; } } if (key->iqmp.data == NULL) { key->iqmp.len = BN_num_bytes(rsa_iqmp); key->iqmp.data = malloc(key->iqmp.len); if (key->iqmp.data) { BN_bn2bin(rsa_iqmp, key->iqmp.data); } else { key->iqmp.len = 0; } } end: BN_clear_free(rsa_n); BN_clear_free(rsa_e); BN_clear_free(rsa_d); BN_clear_free(rsa_p); BN_clear_free(rsa_q); 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 r; } static int 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_GOSTR3410: /* for now */ return 0; } return 0; } static int prkey_bits(struct sc_pkcs15_card *p15card, struct sc_pkcs15_prkey *key) { struct sc_context *ctx = p15card->card->ctx; switch (key->algorithm) { case SC_ALGORITHM_RSA: 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, "Unsupported key (keybits %"SC_FORMAT_LEN_SIZE_T"u)", sc_pkcs15init_keybits(&key->u.gostr3410.d)); return SC_ERROR_OBJECT_NOT_VALID; } 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); if (key->u.ec.params.field_length == 0) { sc_log(ctx, "Invalid EC key length"); return SC_ERROR_OBJECT_NOT_VALID; } return (int)key->u.ec.params.field_length; } sc_log(ctx, "Unsupported key algorithm."); return SC_ERROR_NOT_SUPPORTED; } static int 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_GOSTR3410: return SC_PKCS15_TYPE_PRKEY_GOSTR3410; case SC_ALGORITHM_EC: return SC_PKCS15_TYPE_PRKEY_EC; 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."); return SC_ERROR_NOT_SUPPORTED; } static struct sc_pkcs15_df * find_df_by_type(struct sc_pkcs15_card *p15card, unsigned int type) { struct sc_pkcs15_df *df = p15card->df_list; while (df != NULL && df->type != type) df = df->next; return df; } int sc_pkcs15init_select_intrinsic_id(struct sc_pkcs15_card *p15card, struct sc_profile *profile, int type, struct sc_pkcs15_id *id_out, void *data) { #ifndef ENABLE_OPENSSL LOG_FUNC_RETURN(p15card->card->ctx, SC_SUCCESS); #else struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_pubkey *pubkey = NULL; unsigned id_style; struct sc_pkcs15_id id; unsigned char *id_data = NULL; size_t id_data_len = 0; int rv, allocated = 0; LOG_FUNC_CALLED(ctx); if (!id_out || !profile) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); id_style = profile->id_style; /* ID already exists */ if (id_out->len) LOG_FUNC_RETURN(ctx, SC_SUCCESS); /* Native ID style is not intrinsic one */ if (id_style == SC_PKCS15INIT_ID_STYLE_NATIVE) LOG_FUNC_RETURN(ctx, SC_SUCCESS); memset(&id, 0, sizeof(id)); /* Get PKCS15 public key */ switch(type) { case SC_PKCS15_TYPE_CERT_X509: rv = sc_pkcs15_pubkey_from_cert(ctx, (struct sc_pkcs15_der *)data, &pubkey); LOG_TEST_RET(ctx, rv, "X509 parse error"); allocated = 1; break; case SC_PKCS15_TYPE_PRKEY: rv = sc_pkcs15_pubkey_from_prvkey(ctx, (struct sc_pkcs15_prkey *)data, &pubkey); LOG_TEST_RET(ctx, rv, "Cannot get public key"); allocated = 1; break; case SC_PKCS15_TYPE_PUBKEY: pubkey = (struct sc_pkcs15_pubkey *)data; allocated = 0; break; default: sc_log(ctx, "Intrinsic ID is not implemented for the object type 0x%X", type); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } /* 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_GOSTR3410 && !pubkey->u.gostr3410.xy.data) goto done; else if (pubkey->algorithm == SC_ALGORITHM_EC && !pubkey->u.ec.ecpointQ.value) goto done; /* In Mozilla 'GOST R 34.10' is not yet supported. * So, switch to the ID recommended by RFC2459 */ if (pubkey->algorithm == SC_ALGORITHM_GOSTR3410 && id_style == SC_PKCS15INIT_ID_STYLE_MOZILLA) id_style = SC_PKCS15INIT_ID_STYLE_RFC2459; switch (id_style) { 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_EC) /* ID should be SHA1 of the X coordinate according to PKCS#15 v1.1 */ /* skip the 04 tag and get the X component */ SHA1(pubkey->u.ec.ecpointQ.value+1, (pubkey->u.ec.ecpointQ.len - 1) / 2, id.value); else goto done; id.len = SHA_DIGEST_LENGTH; break; case SC_PKCS15INIT_ID_STYLE_RFC2459: rv = sc_pkcs15_encode_pubkey(ctx, pubkey, &id_data, &id_data_len); LOG_TEST_GOTO_ERR(ctx, rv, "Encoding public key error"); if (!id_data || !id_data_len) { rv = SC_ERROR_INTERNAL; LOG_TEST_GOTO_ERR(ctx, rv, "Encoding public key error"); } SHA1(id_data, id_data_len, id.value); id.len = SHA_DIGEST_LENGTH; break; default: sc_log(ctx, "Unsupported ID style: %i", id_style); rv = SC_ERROR_NOT_SUPPORTED; LOG_TEST_GOTO_ERR(ctx, rv, "Non supported ID style"); } done: memcpy(id_out, &id, sizeof(*id_out)); rv = (int)id_out->len; err: if (id_data) free(id_data); if (allocated) sc_pkcs15_free_pubkey(pubkey); LOG_FUNC_RETURN(ctx, rv); #endif } static int select_id(struct sc_pkcs15_card *p15card, int type, struct sc_pkcs15_id *id) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_id unused_id; struct sc_pkcs15_object *obj; unsigned int nid = DEFAULT_ID; int r; LOG_FUNC_CALLED(ctx); /* If the user provided an ID, make sure we can use it */ if (id->len != 0) { r = sc_pkcs15_find_object_by_id(p15card, type, id, &obj); if (r == SC_ERROR_OBJECT_NOT_FOUND) r = 0; else if (!r) r = SC_ERROR_NON_UNIQUE_ID; LOG_FUNC_RETURN(ctx, r); } memset(&unused_id, 0, sizeof(unused_id)); while (nid < 255) { id->value[0] = nid++; id->len = 1; r = sc_pkcs15_find_object_by_id(p15card, type, id, &obj); if (r == SC_ERROR_OBJECT_NOT_FOUND) { /* We don't have an object of that type yet. * If we're allocating a PRKEY object, make * sure there's no conflicting pubkey or cert * object either. */ if (type == SC_PKCS15_TYPE_PRKEY) { struct sc_pkcs15_search_key search_key; memset(&search_key, 0, sizeof(search_key)); search_key.class_mask = SC_PKCS15_SEARCH_CLASS_PUBKEY | SC_PKCS15_SEARCH_CLASS_CERT; search_key.id = id; r = sc_pkcs15_search_objects(p15card, &search_key, NULL, 0); /* If there is a pubkey or cert with * this ID, skip it. */ if (r > 0) continue; } if (!unused_id.len) unused_id = *id; continue; } } if (unused_id.len) { *id = unused_id; LOG_FUNC_RETURN(ctx, 0); } LOG_FUNC_RETURN(ctx, SC_ERROR_TOO_MANY_OBJECTS); } /* * Select a path for a new object * 1. If the object is to be protected by a PIN, use the path * given in the PIN auth object * 2. Otherwise, use the path of the application DF * 3. If the profile defines a key-dir template, the new object * should go into a subdirectory of the selected DF: * Instantiate the template, using the ID of the new object * to uniquify the path. Inside the instantiated template, * look for a file corresponding to the type of object we * wish to create ("private-key", "public-key" etc). */ static const char * get_template_name_from_object (struct sc_pkcs15_object *obj) { switch (obj->type & SC_PKCS15_TYPE_CLASS_MASK) { case SC_PKCS15_TYPE_PRKEY: return "private-key"; case SC_PKCS15_TYPE_PUBKEY: return "public-key"; case SC_PKCS15_TYPE_SKEY: return "secret-key"; case SC_PKCS15_TYPE_CERT: return "certificate"; case SC_PKCS15_TYPE_DATA_OBJECT: if (obj->flags & SC_PKCS15_CO_FLAG_PRIVATE) return "privdata"; else return "data"; } return NULL; } static int get_object_path_from_object (struct sc_pkcs15_object *obj, struct sc_path *ret_path) { if (!ret_path) return SC_ERROR_INVALID_ARGUMENTS; memset(ret_path, 0, sizeof(struct sc_path)); switch(obj->type & SC_PKCS15_TYPE_CLASS_MASK) { case SC_PKCS15_TYPE_PRKEY: *ret_path = ((struct sc_pkcs15_prkey_info *)obj->data)->path; return SC_SUCCESS; case SC_PKCS15_TYPE_PUBKEY: *ret_path = ((struct sc_pkcs15_pubkey_info *)obj->data)->path; return SC_SUCCESS; case SC_PKCS15_TYPE_SKEY: *ret_path = ((struct sc_pkcs15_skey_info *)obj->data)->path; return SC_SUCCESS; case SC_PKCS15_TYPE_CERT: *ret_path = ((struct sc_pkcs15_cert_info *)obj->data)->path; return SC_SUCCESS; case SC_PKCS15_TYPE_DATA_OBJECT: *ret_path = ((struct sc_pkcs15_data_info *)obj->data)->path; return SC_SUCCESS; case SC_PKCS15_TYPE_AUTH: *ret_path = ((struct sc_pkcs15_auth_info *)obj->data)->path; return SC_SUCCESS; } return SC_ERROR_NOT_SUPPORTED; } static int select_object_path(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15_object *obj, struct sc_path *path) { 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_path obj_path; int ii, r, nn_objs, indx; const char *name; LOG_FUNC_CALLED(ctx); r = sc_pkcs15_get_objects(p15card, obj->type & SC_PKCS15_TYPE_CLASS_MASK, objs, sizeof(objs)/sizeof(objs[0])); LOG_TEST_RET(ctx, r, "Get PKCS#15 objects error"); nn_objs = r; /* For cards with a pin-domain profile, we need * to put the key below the DF of the specified PIN */ memset(path, 0, sizeof(*path)); if (obj->auth_id.len && profile->pin_domains != 0) { r = sc_pkcs15init_get_pin_path(p15card, &obj->auth_id, path); LOG_TEST_RET(ctx, r, "Cannot get PIN path"); } else { *path = profile->df_info->file->path; } /* If the profile specifies a key directory template, * instantiate it now and create the DF */ name = get_template_name_from_object (obj); if (!name) LOG_FUNC_RETURN(ctx, SC_SUCCESS); 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); 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); if (r == SC_ERROR_FILE_NOT_FOUND) LOG_FUNC_RETURN(ctx, SC_SUCCESS); } LOG_TEST_RET(ctx, r, "Template instantiation error"); if (file->type == SC_FILE_TYPE_BSO) break; sc_log(ctx, "instantiated template path %s", sc_print_path(&file->path)); for (ii=0; iipath.len) break; if (!memcmp(obj_path.value, file->path.value, obj_path.len)) break; } if (ii==nn_objs) break; if (obj_path.len != file->path.len) break; sc_file_free(file); indx_id.value[0] += 1; } if (indx > TEMPLATE_INSTANTIATE_MAX_INDEX) LOG_TEST_RET(ctx, SC_ERROR_TOO_MANY_OBJECTS, "Template instantiation error"); *path = file->path; sc_file_free(file); sc_log(ctx, "returns object path '%s'", sc_print_path(path)); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } /* * Update EF(DIR) */ static int sc_pkcs15init_update_dir(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_app_info *app) { struct sc_context *ctx = p15card->card->ctx; struct sc_card *card = p15card->card; int r, retry = 1; LOG_FUNC_CALLED(ctx); if (profile->ops->emu_update_dir) { r = profile->ops->emu_update_dir(profile, p15card, app); LOG_FUNC_RETURN(ctx, r); } do { struct sc_file *dir_file; struct sc_path path; r = sc_enum_apps(card); if (r != SC_ERROR_FILE_NOT_FOUND) break; /* DIR file is not yet created. */ sc_format_path("3F002F00", &path); r = sc_profile_get_file_by_path(profile, &path, &dir_file); LOG_TEST_RET(ctx, r, "DIR file not defined in profile"); /* Create DIR file */ r = sc_pkcs15init_update_file(profile, p15card, dir_file, NULL, 0); sc_file_free(dir_file); } while (retry--); if (r >= 0) { card->app[card->app_count++] = app; r = sc_update_dir(card, NULL); } LOG_FUNC_RETURN(ctx, r); } static int sc_pkcs15init_update_tokeninfo(struct sc_pkcs15_card *p15card, struct sc_profile *profile) { struct sc_context *ctx = p15card->card->ctx; unsigned char *buf = NULL; size_t size; int rv; LOG_FUNC_CALLED(ctx); /* set lastUpdate field */ if (p15card->tokeninfo->last_update.gtime != NULL) { free(p15card->tokeninfo->last_update.gtime); p15card->tokeninfo->last_update.gtime = NULL; } rv = sc_pkcs15_get_generalized_time(ctx, &p15card->tokeninfo->last_update.gtime); LOG_TEST_RET(ctx, rv, "Cannot allocate generalized time string"); if (profile->ops->emu_update_tokeninfo) return profile->ops->emu_update_tokeninfo(profile, p15card, p15card->tokeninfo); if (!p15card->file_tokeninfo) { sc_log(ctx, "No TokenInfo to update"); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } rv = sc_pkcs15_encode_tokeninfo(ctx, p15card->tokeninfo, &buf, &size); if (rv >= 0) rv = sc_pkcs15init_update_file(profile, p15card, p15card->file_tokeninfo, buf, size); if (buf) free(buf); LOG_FUNC_RETURN(ctx, rv); } static int sc_pkcs15init_update_lastupdate(struct sc_pkcs15_card *p15card, struct sc_profile *profile) { struct sc_context *ctx = p15card->card->ctx; int r; LOG_FUNC_CALLED(ctx); if (p15card->tokeninfo->last_update.path.len) { static const struct sc_asn1_entry c_asn1_last_update[2] = { { "generalizedTime", SC_ASN1_GENERALIZEDTIME, SC_ASN1_TAG_GENERALIZEDTIME, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; struct sc_asn1_entry asn1_last_update[2]; size_t lupdate_len; struct sc_file *file = NULL; struct sc_pkcs15_last_update *last_update = &p15card->tokeninfo->last_update; unsigned char *buf = NULL; size_t buflen; /* update 'lastUpdate' file */ if (last_update->gtime != NULL) free(last_update->gtime); r = sc_pkcs15_get_generalized_time(ctx, &last_update->gtime); LOG_TEST_RET(ctx, r, "Cannot allocate generalized time string"); sc_copy_asn1_entry(c_asn1_last_update, asn1_last_update); lupdate_len = strlen(last_update->gtime); sc_format_asn1_entry(asn1_last_update + 0, last_update->gtime, &lupdate_len, 1); r = sc_asn1_encode(ctx, asn1_last_update, &buf, &buflen); 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); sc_file_free(file); if (buf) free(buf); LOG_TEST_RET(ctx, r, "Cannot update 'LastUpdate' file"); LOG_FUNC_RETURN(ctx, r); } r = sc_pkcs15init_update_tokeninfo(p15card, profile); LOG_FUNC_RETURN(ctx, r); } static int sc_pkcs15init_update_odf(struct sc_pkcs15_card *p15card, struct sc_profile *profile) { struct sc_context *ctx = p15card->card->ctx; unsigned char *buf = NULL; size_t size; int r; LOG_FUNC_CALLED(ctx); r = sc_pkcs15_encode_odf(ctx, p15card, &buf, &size); if (r >= 0) r = sc_pkcs15init_update_file(profile, p15card, p15card->file_odf, buf, size); if (buf) free(buf); LOG_FUNC_RETURN(ctx, r); } /* * Update any PKCS15 DF file (except ODF and DIR) */ int sc_pkcs15init_update_any_df(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15_df *df, int is_new) { struct sc_context *ctx = p15card->card->ctx; struct sc_card *card = p15card->card; struct sc_file *file = NULL; unsigned char *buf = NULL; size_t bufsize = 0; int update_odf = is_new, r = 0; LOG_FUNC_CALLED(ctx); if (!df) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "DF missing"); r = sc_profile_get_file_by_path(profile, &df->path, &file); if (r < 0 || file == NULL) sc_select_file(card, &df->path, &file); r = sc_pkcs15_encode_df(card->ctx, p15card, df, &buf, &bufsize); if (r >= 0) { r = sc_pkcs15init_update_file(profile, p15card, file, buf, bufsize); /* For better performance and robustness, we want * to note which portion of the file actually * contains valid data. * * This is particularly useful if we store certificates * directly in the CDF - we may want to make the CDF * fairly big, without having to read the entire file * every time we parse the CDF. */ if (profile->pkcs15.encode_df_length) { df->path.count = (int)bufsize; df->path.index = 0; update_odf = 1; } free(buf); } sc_file_free(file); LOG_TEST_RET(ctx, r, "Failed to encode or update xDF"); /* Now update the ODF if we have to */ if (update_odf) r = sc_pkcs15init_update_odf(p15card, profile); LOG_TEST_RET(ctx, r, "Failed to encode or update ODF"); LOG_FUNC_RETURN(ctx, r > 0 ? SC_SUCCESS : r); } /* * Add an object to one of the pkcs15 directory files. */ int sc_pkcs15init_add_object(struct sc_pkcs15_card *p15card, struct sc_profile *profile, unsigned int df_type, struct sc_pkcs15_object *object) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_df *df; int is_new = 0, r = 0, object_added = 0; LOG_FUNC_CALLED(ctx); sc_log(ctx, "add object %p to DF of type %u", object, df_type); df = find_df_by_type(p15card, df_type); if (df == NULL) { struct sc_file *file; file = profile->df[df_type]; if (file == NULL) { sc_log(ctx, "Profile doesn't define a DF file %u", df_type); LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "DF not found in profile"); } sc_pkcs15_add_df(p15card, df_type, &file->path); df = find_df_by_type(p15card, df_type); assert(df != NULL); is_new = 1; /* Mark the df as enumerated, so libopensc doesn't try * to load the file at a most inconvenient moment */ df->enumerated = 1; } if (object == NULL) { sc_log(ctx, "Add nothing; just instantiate this directory file"); } else if (object->df == NULL) { sc_log(ctx, "Append object"); object->df = df; r = sc_pkcs15_add_object(p15card, object); LOG_TEST_RET(ctx, r, "Failed to add pkcs15 object"); object_added = 1; } else { sc_log(ctx, "Reuse existing object"); assert(object->df == df); } if (profile->ops->emu_update_any_df) r = profile->ops->emu_update_any_df(profile, p15card, SC_AC_OP_CREATE, object); else r = sc_pkcs15init_update_any_df(p15card, profile, df, is_new); if (r < 0 && object_added) sc_pkcs15_remove_object(p15card, object); LOG_FUNC_RETURN(ctx, r > 0 ? SC_SUCCESS : r); } struct sc_pkcs15_object * sc_pkcs15init_new_object(int type, const char *label, struct sc_pkcs15_id *auth_id, void *data) { struct sc_pkcs15_object *object; unsigned int data_size = 0; object = calloc(1, sizeof(*object)); if (object == NULL) return NULL; object->type = type; switch (type & SC_PKCS15_TYPE_CLASS_MASK) { case SC_PKCS15_TYPE_AUTH: object->flags = DEFAULT_PIN_FLAGS; data_size = sizeof(struct sc_pkcs15_auth_info); break; case SC_PKCS15_TYPE_PRKEY: object->flags = DEFAULT_PRKEY_FLAGS; data_size = sizeof(struct sc_pkcs15_prkey_info); break; case SC_PKCS15_TYPE_SKEY: object->flags = DEFAULT_SKEY_FLAGS; data_size = sizeof(struct sc_pkcs15_skey_info); break; case SC_PKCS15_TYPE_PUBKEY: object->flags = DEFAULT_PUBKEY_FLAGS; data_size = sizeof(struct sc_pkcs15_pubkey_info); break; case SC_PKCS15_TYPE_CERT: object->flags = DEFAULT_CERT_FLAGS; data_size = sizeof(struct sc_pkcs15_cert_info); break; case SC_PKCS15_TYPE_DATA_OBJECT: object->flags = DEFAULT_DATA_FLAGS; if (auth_id->len != 0) object->flags |= SC_PKCS15_CO_FLAG_PRIVATE; data_size = sizeof(struct sc_pkcs15_data_info); break; } if (data_size) { object->data = calloc(1, data_size); if (data) memcpy(object->data, data, data_size); } if (label) strlcpy(object->label, label, sizeof(object->label)); if (auth_id) object->auth_id = *auth_id; return object; } void sc_pkcs15init_free_object(struct sc_pkcs15_object *object) { if (object) { free(object->data); free(object); } } int sc_pkcs15init_change_attrib(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15_object *object, int new_attrib_type, void *new_value, int new_len) { struct sc_context *ctx = p15card->card->ctx; struct sc_card *card = p15card->card; unsigned char *buf = NULL; 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); LOG_FUNC_CALLED(ctx); if (object == NULL || object->df == NULL) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Cannot change attribute"); df_type = object->df->type; df = find_df_by_type(p15card, df_type); if (df == NULL) LOG_TEST_RET(ctx, SC_ERROR_OBJECT_NOT_FOUND, "Cannot change attribute"); sc_log(ctx, "type of attribute to change %i; DF type %i", new_attrib_type, df_type); switch(new_attrib_type) { case P15_ATTR_TYPE_LABEL: if (new_len >= SC_PKCS15_MAX_LABEL_SIZE) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "New label too long"); memcpy(object->label, new_value, new_len); object->label[new_len] = '\0'; break; case P15_ATTR_TYPE_ID: switch(df_type) { case SC_PKCS15_PRKDF: ((struct sc_pkcs15_prkey_info *) object->data)->id = new_id; break; case SC_PKCS15_PUKDF: case SC_PKCS15_PUKDF_TRUSTED: ((struct sc_pkcs15_pubkey_info *) object->data)->id = new_id; break; case SC_PKCS15_SKDF: ((struct sc_pkcs15_skey_info *) object->data)->id = new_id; break; case SC_PKCS15_CDF: case SC_PKCS15_CDF_TRUSTED: case SC_PKCS15_CDF_USEFUL: ((struct sc_pkcs15_cert_info *) object->data)->id = new_id; break; default: 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' or 'VALUE'(for data objects) attributes can be changed"); } if (profile->ops->emu_update_any_df) { r = profile->ops->emu_update_any_df(profile, p15card, SC_AC_OP_CREATE, object); LOG_TEST_RET(ctx, r, "Card specific DF update failed"); } else { r = sc_pkcs15_encode_df(card->ctx, p15card, df, &buf, &bufsize); if (r >= 0) { struct sc_file *file = NULL; r = sc_profile_get_file_by_path(profile, &df->path, &file); if (r < 0) free(buf); LOG_TEST_RET(ctx, r, "Cannot instantiate file by path"); r = sc_pkcs15init_update_file(profile, p15card, file, buf, bufsize); free(buf); sc_file_free(file); } } if (r > 0) r = 0; LOG_FUNC_RETURN(ctx, r); } int sc_pkcs15init_delete_object(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15_object *obj) { struct sc_context *ctx = p15card->card->ctx; struct sc_file *file = NULL; struct sc_path path; struct sc_pkcs15_df *df; int r = 0, stored_in_ef = 0; LOG_FUNC_CALLED(ctx); r = get_object_path_from_object(obj, &path); LOG_TEST_RET(ctx, r, "Failed to get object path"); sc_log(ctx, "delete object(type:%X) with path(type:%X,%s)", obj->type, path.type, sc_print_path(&path)); if (profile->ops->delete_object != NULL) { /* If there's a card-specific way to delete objects, use it. */ r = profile->ops->delete_object(profile, p15card, obj, &path); if (r != SC_ERROR_NOT_SUPPORTED) LOG_TEST_RET(ctx, r, "Card specific delete object failed"); } if (profile->ops->delete_object == NULL || r == SC_ERROR_NOT_SUPPORTED) { if (path.len || path.aid.len) { r = sc_select_file(p15card->card, &path, &file); if (r != SC_ERROR_FILE_NOT_FOUND) LOG_TEST_RET(ctx, r, "select object path failed"); stored_in_ef = (file->type != SC_FILE_TYPE_DF); sc_file_free(file); } /* If the object is stored in a normal EF, try to delete the EF. */ if (r == SC_SUCCESS && stored_in_ef) { r = sc_pkcs15init_delete_by_path(profile, p15card, &path); LOG_TEST_RET(ctx, r, "Failed to delete object by path"); } } if (profile->ops->emu_update_any_df) { r = profile->ops->emu_update_any_df(profile, p15card, SC_AC_OP_ERASE, obj); LOG_TEST_RET(ctx, r, "'ERASE' update DF failed"); } /* Get the DF we're part of. If there's no DF, fine, we haven't been added yet. */ df = obj->df; if (df) { /* Unlink the object and update the DF */ sc_pkcs15_remove_object(p15card, obj); sc_pkcs15_free_object(obj); } if (!profile->ops->emu_update_any_df) r = sc_pkcs15init_update_any_df(p15card, profile, df, 0); /* mark card as dirty */ profile->dirty = 1; LOG_FUNC_RETURN(ctx, r); } int sc_pkcs15init_update_certificate(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15_object *obj, const unsigned char *rawcert, size_t certlen) { struct sc_context *ctx = p15card->card->ctx; struct sc_file *file = NULL; struct sc_path *path = &((struct sc_pkcs15_cert_info *)obj->data)->path; int r; LOG_FUNC_CALLED(ctx); r = sc_select_file(p15card->card, path, &file); LOG_TEST_RET(ctx, r, "Failed to select cert file"); /* If the new cert doesn't fit in the EF, delete it and make the same, but bigger EF */ if (file->size != certlen) { struct sc_file *parent = NULL; r = sc_pkcs15init_delete_by_path(profile, p15card, path); if (r < 0) goto done; file->size = certlen; r = do_select_parent(profile, p15card, file, &parent); if (r < 0) goto done; r = sc_pkcs15init_authenticate(profile, p15card, parent, SC_AC_OP_CREATE); sc_file_free(parent); if (r < 0) { sc_log(ctx, "'CREATE' authentication failed"); goto done; } /* ensure we are in the correct lifecycle */ r = sc_pkcs15init_set_lifecycle(p15card->card, SC_CARDCTRL_LIFECYCLE_ADMIN); if (r < 0 && r != SC_ERROR_NOT_SUPPORTED) goto done; r = sc_create_file(p15card->card, file); if (r < 0) { sc_log(ctx, "Cannot create cert file"); goto done; } } if (!sc_file_get_acl_entry(file, SC_AC_OP_UPDATE)) { struct sc_path tmp_path; /* FCI of selected cert file do not contains ACLs. * For the 'UPDATE' authentication use instead sc_file * instantiated from card profile with default ACLs. */ sc_file_free(file); r = select_object_path(p15card, profile, obj, &tmp_path); if (r < 0) { sc_log(ctx, "Select object path error"); goto done; } r = sc_profile_get_file_by_path(profile, path, &file); if (r < 0) { sc_log(ctx, "Cannot instantiate cert file"); goto done; } } /* Write the new cert */ r = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_UPDATE); if (r < 0) { sc_log(ctx, "'UPDATE' authentication failed"); goto done; } r = sc_select_file(p15card->card, path, NULL); if (r < 0) goto done; r = sc_update_binary(p15card->card, 0, rawcert, certlen, 0); if (r < 0) goto done; /* Fill the remaining space in the EF (if any) with zeros */ if (certlen < file->size) { 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, (unsigned int)certlen, tmp, file->size - certlen, 0); free(tmp); if (r < 0) sc_log(ctx, "Update cert file error"); } if (r >= 0) { /* Update the CDF entry */ path = &((struct sc_pkcs15_cert_info *)obj->data)->path; if (file->size != certlen) { path->index = 0; path->count = (int)certlen; } else { path->count = -1; } if (profile->ops->emu_update_any_df) { r = profile->ops->emu_update_any_df(profile, p15card, SC_AC_OP_UPDATE, obj); if (r == SC_ERROR_NOT_SUPPORTED) r = SC_SUCCESS; } else { r = sc_pkcs15init_update_any_df(p15card, profile, obj->df, 0); } if (r < 0) sc_log(ctx, "Failed to update CDF"); } /* mark card as dirty */ profile->dirty = 1; done: sc_file_free(file); LOG_FUNC_RETURN(ctx, r); } static const char * get_pin_ident_name(int type, int reference) { switch (type) { case SC_AC_CHV: return "PIN"; case SC_AC_PRO: return "secure messaging key"; case SC_AC_AUT: return "authentication key"; case SC_AC_SEN: return "security environment"; case SC_AC_IDA: return "PKCS#15 reference"; case SC_AC_SCB: return "SCB byte in IAS/ECC"; case SC_AC_SYMBOLIC: switch (reference) { case SC_PKCS15INIT_USER_PIN: return "user PIN"; case SC_PKCS15INIT_SO_PIN: return "SO PIN"; case SC_PKCS15INIT_USER_PUK: return "user PUK"; case SC_PKCS15INIT_SO_PUK: return "SO PUK"; } } return "authentication data"; } static int sc_pkcs15init_get_transport_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, int type, int reference, unsigned char *pinbuf, size_t *pinsize) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_object *pin_obj = NULL; struct sc_pkcs15_auth_info auth_info; struct sc_cardctl_default_key data; size_t defsize = 0; unsigned char defbuf[0x100]; int rv; LOG_FUNC_CALLED(ctx); data.method = type; data.key_ref = reference; data.len = sizeof(defbuf); data.key_data = defbuf; rv = sc_card_ctl(p15card->card, SC_CARDCTL_GET_DEFAULT_KEY, &data); if (rv >= 0) defsize = data.len; 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) { 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)); auth_info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; auth_info.auth_method = type; auth_info.attrs.pin.reference = reference; auth_info.attrs.pin.stored_length = *pinsize; auth_info.attrs.pin.max_length = *pinsize; auth_info.attrs.pin.min_length = *pinsize; pin_obj = sc_pkcs15init_new_object(SC_PKCS15_TYPE_AUTH_PIN, "Default transport key", NULL, &auth_info); if (!pin_obj) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot allocate AUTH object"); rv = sc_pkcs15_add_object(p15card, pin_obj); LOG_TEST_RET(ctx, rv, "Cannot add PKCS#15 AUTH object"); sc_pkcs15_pincache_add(p15card, pin_obj, pinbuf, *pinsize); LOG_FUNC_RETURN(ctx, rv); } /* * PIN verification */ int sc_pkcs15init_verify_secret(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_file *file, unsigned int type, int reference) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_object *pin_obj = NULL; struct sc_pkcs15_auth_info auth_info; struct sc_path *path; int r, use_pinpad = 0, pin_id = -1; const char *ident, *label = NULL; unsigned char pinbuf[0x100]; size_t pinsize = 0; LOG_FUNC_CALLED(ctx); path = file? &file->path : NULL; ident = get_pin_ident_name(type, reference); sc_log(ctx, "get and verify PIN('%s',type:0x%X,reference:0x%X)", ident, type, reference); if (type == SC_AC_SEN) { r = sc_card_ctl(p15card->card, SC_CARDCTL_GET_CHV_REFERENCE_IN_SE, (void *)(&reference)); sc_log(ctx, "Card CTL(GET_CHV_REFERENCE_IN_SE) returned %i", r); if (r > 0) { sc_log(ctx, "CHV(ref:%i) found in SE(ref:%i)", r, reference); type = SC_AC_CHV; reference = r; } else if (r != SC_ERROR_NOT_SUPPORTED) LOG_TEST_RET(ctx, r, "Card CTL error: cannot get CHV reference"); } memset(&auth_info, 0, sizeof(auth_info)); auth_info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; auth_info.auth_method = type; auth_info.attrs.pin.reference = reference; pin_id = sc_pkcs15init_get_pin_reference(p15card, profile, type, reference); sc_log(ctx, "found PIN reference %i", pin_id); if (type == SC_AC_SYMBOLIC) { if (pin_id == -1) LOG_FUNC_RETURN(ctx, SC_SUCCESS); reference = pin_id; type = SC_AC_CHV; sc_log(ctx, "Symbolic PIN resolved to PIN(type:CHV,reference:%i)", reference); } 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 = (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); } if (!r && pin_obj) { memcpy(&auth_info, pin_obj->data, sizeof(auth_info)); sc_log(ctx, "found PIN object '%.*s'", (int) sizeof pin_obj->label, pin_obj->label); } if (pin_obj) { sc_log(ctx, "PIN object '%.*s'; pin_obj->content.len:%"SC_FORMAT_LEN_SIZE_T"u", (int) sizeof pin_obj->label, pin_obj->label, pin_obj->content.len); if (pin_obj->content.value && pin_obj->content.len) { if (pin_obj->content.len > sizeof(pinbuf)) LOG_TEST_RET(ctx, SC_ERROR_BUFFER_TOO_SMALL, "PIN buffer is too small"); memcpy(pinbuf, pin_obj->content.value, pin_obj->content.len); pinsize = pin_obj->content.len; sc_log(ctx, "'ve got '%s' value from cache", ident); goto found; } } if (pin_obj && pin_obj->label[0]) label = pin_obj->label; switch (type) { case SC_AC_CHV: if (callbacks.get_pin) { pinsize = sizeof(pinbuf); r = callbacks.get_pin(profile, pin_id, &auth_info, label, pinbuf, &pinsize); sc_log(ctx, "'get_pin' callback returned %i; pinsize:%"SC_FORMAT_LEN_SIZE_T"u", r, pinsize); } break; case SC_AC_SCB: case SC_AC_PRO: pinsize = 0; r = 0; break; default: pinsize = sizeof(pinbuf); r = sc_pkcs15init_get_transport_key(profile, p15card, type, reference, pinbuf, &pinsize); break; } if (r == SC_ERROR_OBJECT_NOT_FOUND) { if (p15card->card->reader->capabilities & SC_READER_CAP_PIN_PAD) r = 0, use_pinpad = 1; else r = SC_ERROR_SECURITY_STATUS_NOT_SATISFIED; } LOG_TEST_RET(ctx, r, "Failed to get secret"); if (type == SC_AC_PRO) { sc_log(ctx, "No 'verify' for secure messaging"); LOG_FUNC_RETURN(ctx, r); } found: if (pin_obj) { /* * 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"); } if (file) { r = sc_select_file(p15card->card, &file->path, NULL); LOG_TEST_RET(ctx, r, "Failed to select PIN path"); } if (!pin_obj) { struct sc_pin_cmd_data pin_cmd; memset(&pin_cmd, 0, sizeof(pin_cmd)); pin_cmd.cmd = SC_PIN_CMD_VERIFY; pin_cmd.pin_type = type; pin_cmd.pin_reference = reference; 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); LOG_TEST_RET(ctx, r, "'VERIFY' pin cmd failed"); } LOG_FUNC_RETURN(ctx, r); } /* * Present any authentication info as required by the file. * * Depending on the SC_CARD_CAP_USE_FCI_AC caps file in sc_card_t, * we read the ACs of the file on the card, or rely on the ACL * info for that file in the profile file. * * 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 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. */ int sc_pkcs15init_authenticate(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_file *file, int op) { struct sc_context *ctx = p15card->card->ctx; const struct sc_acl_entry *acl = NULL; struct sc_file *file_tmp = NULL; int r = 0; LOG_FUNC_CALLED(ctx); assert(file != NULL); 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."); acl = sc_file_get_acl_entry(file_tmp, op); } else { acl = sc_file_get_acl_entry(file, op); } sc_log(ctx, "acl %p",acl); 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) { sc_log(ctx, "always allowed"); break; } else if (acl->method == SC_AC_UNKNOWN) { sc_log(ctx, "unknown acl method"); break; } sc_log(ctx, "verify acl(method:%i,reference:%i)", acl->method, acl->key_ref); r = sc_pkcs15init_verify_secret(profile, p15card, file_tmp ? file_tmp : file, acl->method, acl->key_ref); } sc_file_free(file_tmp); LOG_FUNC_RETURN(ctx, r); } static int do_select_parent(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_file *file, struct sc_file **parent) { struct sc_context *ctx = p15card->card->ctx; struct sc_path path; int r; LOG_FUNC_CALLED(ctx); /* Get the parent's path */ path = file->path; if (path.len >= 2) path.len -= 2; if (!path.len && !path.aid.len) sc_format_path("3F00", &path); /* Select the parent DF. */ *parent = NULL; 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) { 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)); LOG_FUNC_RETURN(ctx, r); } 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")) { /* in case of starcos spk 2.3 SELECT FILE does not * give us the ACLs => ask the profile */ sc_file_free(*parent); r = sc_profile_get_file_by_path(profile, &path, parent); if (r < 0) { sc_log(ctx, "in StarCOS profile there is no template for DF %s", sc_print_path(&path)); LOG_FUNC_RETURN(ctx, r); } } LOG_FUNC_RETURN(ctx, r); } int sc_pkcs15init_create_file(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_file *file) { struct sc_context *ctx = p15card->card->ctx; struct sc_file *parent = NULL; 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_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_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_GOTO_ERR(ctx, r, "Cannot create file: failed to set lifecycle 'ADMIN'"); r = sc_create_file(p15card->card, file); LOG_TEST_GOTO_ERR(ctx, r, "Create file failed"); err: sc_file_free(parent); LOG_FUNC_RETURN(ctx, r); } int sc_pkcs15init_update_file(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_file *file, void *data, size_t datalen) { struct sc_context *ctx = p15card->card->ctx; struct sc_file *selected_file = NULL; void *copy = NULL; int r, need_to_zap = 0; LOG_FUNC_CALLED(ctx); if (!file) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); 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) { need_to_zap = 1; } else if (r == SC_ERROR_FILE_NOT_FOUND) { /* Create file if it doesn't exist */ if (file->size < datalen) file->size = datalen; r = sc_pkcs15init_create_file(profile, p15card, file); LOG_TEST_RET(ctx, r, "Failed to create file"); r = sc_select_file(p15card->card, &file->path, &selected_file); LOG_TEST_RET(ctx, r, "Failed to select newly created file"); } else { LOG_TEST_RET(ctx, r, "Failed to select file"); } if (selected_file->size < datalen) { sc_log(ctx, "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); LOG_TEST_RET(ctx, SC_ERROR_FILE_TOO_SMALL, "Update file failed"); } 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); return SC_ERROR_OUT_OF_MEMORY; } memcpy(copy, data, datalen); datalen = selected_file->size; data = copy; } /* Present authentication info needed */ 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); if (copy) free(copy); sc_file_free(selected_file); LOG_FUNC_RETURN(ctx, r); } /* * Fix up a file's ACLs by replacing all occurrences of a symbolic * PIN name with the real reference. */ static int sc_pkcs15init_fixup_acls(struct sc_pkcs15_card *p15card, struct sc_file *file, struct sc_acl_entry *so_acl, struct sc_acl_entry *user_acl) { struct sc_context *ctx = p15card->card->ctx; unsigned int op; int r = 0; LOG_FUNC_CALLED(ctx); for (op = 0; r == 0 && op < SC_MAX_AC_OPS; op++) { struct sc_acl_entry acls[SC_MAX_OP_ACS]; const struct sc_acl_entry *acl; const char *what; int added = 0, num, ii; /* First, get original ACLs */ acl = sc_file_get_acl_entry(file, op); for (num = 0; num < SC_MAX_OP_ACS && acl; num++, acl = acl->next) acls[num] = *acl; sc_file_clear_acl_entries(file, op); for (ii = 0; ii < num; ii++) { acl = acls + ii; if (acl->method != SC_AC_SYMBOLIC) goto next; if (acl->key_ref == SC_PKCS15INIT_SO_PIN) { acl = so_acl; what = "SO PIN"; } else if (acl->key_ref == SC_PKCS15INIT_USER_PIN) { acl = user_acl; what = "user PIN"; } else { sc_log(ctx, "ACL references unknown symbolic PIN %d", acl->key_ref); return SC_ERROR_INVALID_ARGUMENTS; } /* If we weren't given a replacement ACL, * leave the original ACL untouched */ if (acl->key_ref == (unsigned int)-1) { sc_log(ctx, "ACL references %s, which is not defined", what); return SC_ERROR_INVALID_ARGUMENTS; } if (acl->method == SC_AC_NONE) continue; next: sc_file_add_acl_entry(file, op, acl->method, acl->key_ref); added++; } if (!added) sc_file_add_acl_entry(file, op, SC_AC_NONE, 0); } LOG_FUNC_RETURN(ctx, r); } /* * Fix up all file ACLs */ int sc_pkcs15init_fixup_file(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_file *file) { struct sc_context *ctx = profile->card->ctx; struct sc_acl_entry so_acl, user_acl; unsigned int op, needfix = 0; int rv, pin_ref; LOG_FUNC_CALLED(ctx); /* First, loop over all ACLs to find out whether there * are still any symbolic references. */ for (op = 0; op < SC_MAX_AC_OPS; op++) { const struct sc_acl_entry *acl; acl = sc_file_get_acl_entry(file, op); for (; acl; acl = acl->next) if (acl->method == SC_AC_SYMBOLIC) needfix++; } if (!needfix) LOG_FUNC_RETURN(ctx, SC_SUCCESS); pin_ref = sc_pkcs15init_get_pin_reference(p15card, profile, SC_AC_SYMBOLIC, SC_PKCS15INIT_SO_PIN); if (pin_ref < 0) { so_acl.method = SC_AC_NONE; so_acl.key_ref = 0; } else { so_acl.method = SC_AC_CHV; so_acl.key_ref = pin_ref; } pin_ref = sc_pkcs15init_get_pin_reference(p15card, profile, SC_AC_SYMBOLIC, SC_PKCS15INIT_USER_PIN); if (pin_ref < 0) { user_acl.method = SC_AC_NONE; user_acl.key_ref = 0; } else { user_acl.method = SC_AC_CHV; user_acl.key_ref = pin_ref; } sc_log(ctx, "so_acl(method:%X,ref:%X), user_acl(method:%X,ref:%X)", so_acl.method, so_acl.key_ref, user_acl.method, user_acl.key_ref); rv = sc_pkcs15init_fixup_acls(p15card, file, &so_acl, &user_acl); LOG_FUNC_RETURN(ctx, rv); } static int sc_pkcs15init_get_pin_path(struct sc_pkcs15_card *p15card, struct sc_pkcs15_id *auth_id, struct sc_path *path) { struct sc_pkcs15_object *obj; int r; r = sc_pkcs15_find_pin_by_auth_id(p15card, auth_id, &obj); if (r < 0) return r; *path = ((struct sc_pkcs15_auth_info *) obj->data)->path; return SC_SUCCESS; } int sc_pkcs15init_get_pin_info(struct sc_profile *profile, int id, struct sc_pkcs15_auth_info *pin) { sc_profile_get_pin_info(profile, id, pin); return SC_SUCCESS; } int sc_pkcs15init_get_manufacturer(struct sc_profile *profile, const char **res) { *res = profile->p15_spec->tokeninfo->manufacturer_id; return SC_SUCCESS; } int sc_pkcs15init_get_serial(struct sc_profile *profile, const char **res) { *res = profile->p15_spec->tokeninfo->serial_number; return SC_SUCCESS; } int sc_pkcs15init_set_serial(struct sc_profile *profile, const char *serial) { if (profile->p15_spec->tokeninfo->serial_number) free(profile->p15_spec->tokeninfo->serial_number); profile->p15_spec->tokeninfo->serial_number = strdup(serial); return SC_SUCCESS; } /* * Card specific sanity check procedure. */ int sc_pkcs15init_sanity_check(struct sc_pkcs15_card *p15card, struct sc_profile *profile) { struct sc_context *ctx = p15card->card->ctx; int rv = SC_ERROR_NOT_SUPPORTED; LOG_FUNC_CALLED(ctx); if (profile->ops->sanity_check) rv = profile->ops->sanity_check(profile, p15card); LOG_FUNC_RETURN(ctx, rv); } static int sc_pkcs15init_qualify_pin(struct sc_card *card, const char *pin_name, 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); pin_attrs = &auth_info->attrs.pin; if (pin_len < pin_attrs->min_length) { sc_log(ctx, "%s too short (min length %"SC_FORMAT_LEN_SIZE_T"u)", pin_name, pin_attrs->min_length); LOG_FUNC_RETURN(ctx, SC_ERROR_WRONG_LENGTH); } if (pin_len > pin_attrs->max_length) { sc_log(ctx, "%s too long (max length %"SC_FORMAT_LEN_SIZE_T"u)", pin_name, pin_attrs->max_length); LOG_FUNC_RETURN(ctx, SC_ERROR_WRONG_LENGTH); } LOG_FUNC_RETURN(ctx, SC_SUCCESS); } /* * Get the list of options from the card, if it specifies them */ static int sc_pkcs15init_read_info(struct sc_card *card, struct sc_profile *profile) { struct sc_path path; struct sc_file *file = NULL; unsigned char *mem = NULL; size_t len = 0; int r; sc_format_path(OPENSC_INFO_FILEPATH, &path); r = sc_select_file(card, &path, &file); 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); else r = SC_ERROR_OUT_OF_MEMORY; } else { r = 0; sc_file_free(file); } if (r >= 0) r = sc_pkcs15init_parse_info(card, mem, r, profile); if (mem) free(mem); return r; } static int set_info_string(char **strp, const u8 *p, size_t len) { char *s; if (!(s = malloc(len+1))) return SC_ERROR_OUT_OF_MEMORY; memcpy(s, p, len); s[len] = '\0'; if (*strp) free(*strp); *strp = s; return SC_SUCCESS; } /* * Parse OpenSC Info file. We rudely clobber any information * given on the command line. * * passed is a pointer (p) to (len) bytes. Those bytes contain * one or several tag-length-value constructs, where tag and * length are both single bytes. a final 0x00 or 0xff byte * (with or without len byte) is ok. */ static int sc_pkcs15init_parse_info(struct sc_card *card, const unsigned char *p, size_t len, struct sc_profile *profile) { unsigned char tag; 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 */ r = 0; tag = *p; p++; if ((tag == 0) || (tag == 0xff) || (p >= end)) break; n = *p; p++; 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) 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"); r = SC_ERROR_PKCS15INIT; goto error; } r = set_info_string(&profile->options[nopts], p, n); if (r < 0) goto error; profile->options[++nopts] = NULL; break; default: /* Unknown options ignored */ ; } p += n; } return 0; error: sc_log(card->ctx, "OpenSC info file corrupted"); 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; } static int do_encode_string(unsigned char **memp, unsigned char *end, unsigned char tag, const char *s) { unsigned char *p = *memp; size_t n; n = s ? strlen(s) : 0; if (n > 255) return SC_ERROR_BUFFER_TOO_SMALL; if (p + 2 + n > end) return SC_ERROR_BUFFER_TOO_SMALL; *p++ = tag; *p++ = n; memcpy(p, s, n); *memp = p + n; return 0; } static int sc_pkcs15init_write_info(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15_object *pin_obj) { struct sc_file *file = NULL, *df = profile->df_info->file; unsigned char buffer[128], *p, *end; unsigned int method; unsigned long key_ref; int n, r; if (profile->ops->emu_write_info) return profile->ops->emu_write_info(profile, p15card, pin_obj); memset(buffer, 0, sizeof(buffer)); file = sc_file_new(); file->path.type = SC_PATH_TYPE_PATH; memcpy(file->path.value, df->path.value, df->path.len); file->path.len = df->path.len; sc_append_file_id(&file->path, OPENSC_INFO_FILEID); file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = SC_FILE_EF_TRANSPARENT; file->id = OPENSC_INFO_FILEID; file->size = sizeof(buffer); if (pin_obj != NULL) { method = SC_AC_CHV; key_ref = ((struct sc_pkcs15_auth_info *) pin_obj->data)->attrs.pin.reference; } else { method = SC_AC_NONE; /* Unprotected */ key_ref = 0; } for (n = 0; n < SC_MAX_AC_OPS; n++) { if (n == SC_AC_OP_READ) sc_file_add_acl_entry(file, n, SC_AC_NONE, 0); else sc_file_add_acl_entry(file, n, method, key_ref); } p = buffer; end = buffer + sizeof(buffer); r = do_encode_string(&p, end, OPENSC_INFO_TAG_PROFILE, profile->name); for (n = 0; r >= 0 && profile->options[n]; n++) 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, (unsigned int)file->size); sc_file_free(file); return r; } OpenSC-0.26.1/src/pkcs15init/pkcs15-muscle.c000066400000000000000000000220701474147347300202670ustar00rootroot00000000000000/* * pkcs15-muscle.c: Support for MuscleCard Applet from musclecard.com * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include "libopensc/pkcs15.h" #include "libopensc/opensc.h" #include "libopensc/cardctl.h" #include "libopensc/cards.h" #include "libopensc/log.h" #include "pkcs15-init.h" #include "profile.h" #define MUSCLE_KEY_ID_MIN 0x00 #define MUSCLE_KEY_ID_MAX 0x0F static int muscle_erase_card(sc_profile_t *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 ((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; } static int muscle_init_card(sc_profile_t *profile, sc_pkcs15_card_t *p15card) { return 0; } static int muscle_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) { 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; if ((r = sc_select_file(p15card->card, &df->path, NULL)) < 0) return r; return 0; } static int muscle_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_file_t *file; sc_pkcs15_auth_info_t *auth_info = (sc_pkcs15_auth_info_t *) pin_obj->data; int r; 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) { sc_file_free(file); return r; } auth_info->attrs.pin.flags &= ~SC_PKCS15_PIN_FLAG_LOCAL; sc_file_free(file); return 0; } static int muscle_select_pin_reference(sc_profile_t *profike, sc_pkcs15_card_t *p15card, sc_pkcs15_auth_info_t *auth_info) { int preferred; 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) { preferred = 0; } else { preferred = 1; } if (auth_info->attrs.pin.reference <= preferred) { auth_info->attrs.pin.reference = preferred; return 0; } if (auth_info->attrs.pin.reference > 2) return SC_ERROR_INVALID_ARGUMENTS; /* Caller, please select a different PIN reference */ return SC_ERROR_INVALID_PIN_REFERENCE; } /* * Select a key reference */ static int muscle_select_key_reference(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_prkey_info_t *key_info) { if (key_info->key_reference < MUSCLE_KEY_ID_MIN) key_info->key_reference = MUSCLE_KEY_ID_MIN; if (key_info->key_reference > MUSCLE_KEY_ID_MAX) return SC_ERROR_TOO_MANY_OBJECTS; return 0; } /* * Create a private key object. * This is a no-op. */ static int muscle_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 muscle_store_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *obj, sc_pkcs15_prkey_t *key) { struct sc_context *ctx = p15card->card->ctx; sc_pkcs15_prkey_info_t *key_info = (sc_pkcs15_prkey_info_t *) obj->data; sc_file_t* prkf; struct sc_pkcs15_prkey_rsa *rsa; sc_cardctl_muscle_key_info_t info; int r; if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) { 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 (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); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE,SC_ERROR_NOT_SUPPORTED); } sc_file_free(prkf); r = muscle_select_key_reference(profile, p15card, key_info); if (r < 0) { SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE,r); } rsa = &key->u.rsa; info.keySize = rsa->modulus.len << 3; info.keyType = 0x03; /* CRT type */ info.keyLocation = key_info->key_reference * 2; /* Mult by 2 to preserve even/odd keynumber structure */ info.pLength = rsa->p.len; info.pValue = rsa->p.data; info.qLength = rsa->q.len; info.qValue = rsa->q.data; info.pqLength = rsa->iqmp.len; info.pqValue = rsa->iqmp.data; info.dp1Length = rsa->dmp1.len; info.dp1Value = rsa->dmp1.data; info.dq1Length = rsa->dmq1.len; info.dq1Value = rsa->dmq1.data; r = sc_card_ctl(p15card->card, SC_CARDCTL_MUSCLE_IMPORT_KEY, &info); if (r < 0) { sc_log(ctx, "Unable to import key"); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE,r); } return r; } static int muscle_generate_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *obj, sc_pkcs15_pubkey_t *pubkey) { sc_cardctl_muscle_gen_key_info_t args; sc_cardctl_muscle_key_info_t extArgs; sc_pkcs15_prkey_info_t *key_info = (sc_pkcs15_prkey_info_t *) obj->data; sc_card_t *card = p15card->card; sc_file_t* prkf; size_t keybits; int r; if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) { 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_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(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); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,SC_ERROR_NOT_SUPPORTED); } sc_file_free(prkf); /* END VERIFICATION STUFF */ /* Public key acls... get_file_by_path as well? */ memset(&args, 0, sizeof(args)); args.keyType = 0x01; /* RSA forced */ args.privateKeyLocation = key_info->key_reference * 2; args.publicKeyLocation = key_info->key_reference * 2 + 1; args.keySize = keybits; r = sc_card_ctl(card, SC_CARDCTL_MUSCLE_GENERATE_KEY, &args); if (r < 0) { sc_log(card->ctx, "Unable to generate key"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,r); } memset(&extArgs, 0, sizeof(extArgs)); memset(pubkey, 0, sizeof(*pubkey)); extArgs.keyType = 0x01; extArgs.keyLocation = args.publicKeyLocation; r = sc_card_ctl(card, SC_CARDCTL_MUSCLE_EXTRACT_KEY, &extArgs); if (r < 0) { sc_log(card->ctx, "Unable to extract the public key"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,r); } pubkey->algorithm = SC_ALGORITHM_RSA; pubkey->u.rsa.modulus.len = extArgs.modLength; pubkey->u.rsa.modulus.data = extArgs.modValue; pubkey->u.rsa.exponent.len = extArgs.expLength; pubkey->u.rsa.exponent.data = extArgs.expValue; return r; } static struct sc_pkcs15init_operations sc_pkcs15init_muscle_operations = { muscle_erase_card, /* erase card */ muscle_init_card, /* init_card */ muscle_create_dir, /* create_dir */ NULL, /* create_domain */ muscle_select_pin_reference, /* select pin reference */ muscle_create_pin, /* Create PIN */ muscle_select_key_reference, /* select_key_reference */ muscle_create_key, /* create_key */ muscle_store_key, /* store_key */ muscle_generate_key, /* 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_muscle_ops(void) { return &sc_pkcs15init_muscle_operations; } OpenSC-0.26.1/src/pkcs15init/pkcs15-myeid.c000066400000000000000000000751251474147347300201170ustar00rootroot00000000000000/* * MyEID specific operations for PKCS15 initialization * * Copyright (C) 2008-2009 Aventra Ltd. * * 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 "libopensc/opensc.h" #include "libopensc/cardctl.h" #include "libopensc/log.h" #include "pkcs15-init.h" #include "profile.h" #include "libopensc/asn1.h" #include "pkcs11/pkcs11.h" #undef KEEP_AC_NONE_FOR_INIT_APPLET #define MYEID_MAX_PINS 14 #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, struct sc_pkcs15_object *object, const struct sc_path *path) { LOG_FUNC_CALLED(p15card->card->ctx); return sc_pkcs15init_delete_by_path(profile, p15card, path); } /* * Get 'Initialize Applet' data * using the ACLs defined in card profile. */ static int myeid_get_init_applet_data(struct sc_profile *profile, struct sc_pkcs15_card *p15card, unsigned char *data, size_t data_len) { struct sc_context *ctx = p15card->card->ctx; struct sc_file *tmp_file = NULL; const struct sc_acl_entry *entry = NULL; int r; LOG_FUNC_CALLED(ctx); if (data_len < 8) LOG_TEST_RET(ctx, SC_ERROR_BUFFER_TOO_SMALL, "Cannot get init applet data"); *(data + 0) = 0xFF; *(data + 1) = 0xFF; /* MF acls */ sc_file_dup(&tmp_file, profile->mf_info->file); if (tmp_file == NULL) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot duplicate MF file"); r = sc_pkcs15init_fixup_file(profile, p15card, tmp_file); if (r < 0) sc_file_free(tmp_file); LOG_TEST_RET(ctx, r, "MF fixup failed"); /* AC 'Create DF' and 'Create EF' */ *(data + 2) = 0x00; /* 'NONE' */ entry = sc_file_get_acl_entry(tmp_file, SC_AC_OP_CREATE); if (entry->method == SC_AC_CHV) *(data + 2) = entry->key_ref | (entry->key_ref << 4); /* 'CHVx'. */ else if (entry->method == SC_AC_NEVER) *(data + 2) = 0xFF; /* 'NEVER'. */ /* AC 'INITIALISE APPLET'. */ *(data + 3) = 0x0F; /* 'NONE' */ #ifndef KEEP_AC_NONE_FOR_INIT_APPLET entry = sc_file_get_acl_entry(tmp_file, SC_AC_OP_DELETE); if (entry->method == SC_AC_CHV) *(data + 3) = (entry->key_ref << 4) | 0xF; else if (entry->method == SC_AC_NEVER) *(data + 3) = 0xFF; #endif *(data + 4) = 0xFF; sc_file_free(tmp_file); tmp_file = NULL; /* Application DF (5015) acls */ sc_file_dup(&tmp_file, profile->df_info->file); if (tmp_file == NULL) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot duplicate Application DF file"); r = sc_pkcs15init_fixup_file(profile, p15card, tmp_file); if (r < 0) sc_file_free(tmp_file); LOG_TEST_RET(ctx, r, "Application DF fixup failed"); /* AC 'Create DF' and 'Create EF' */ *(data + 5) = 0x00; /* 'NONE' */ entry = sc_file_get_acl_entry(tmp_file, SC_AC_OP_CREATE); if (entry->method == SC_AC_CHV) *(data + 5) = entry->key_ref | (entry->key_ref << 4); /* 'CHVx' */ else if (entry->method == SC_AC_NEVER) *(data + 5) = 0xFF; /* 'NEVER'. */ /* AC 'Self delete' */ *(data + 6) = 0x0F; /* 'NONE' */ entry = sc_file_get_acl_entry(tmp_file, SC_AC_OP_DELETE); if (entry->method == SC_AC_CHV) *(data + 6) = (entry->key_ref << 4) | 0xF; /* 'CHVx' */ else if (entry->method == SC_AC_NEVER) *(data + 6) = 0xFF; /* 'NEVER'. */ *(data + 7) = 0xFF; sc_file_free(tmp_file); LOG_FUNC_RETURN(p15card->card->ctx, SC_SUCCESS); } /* * Erase the card. */ static int myeid_erase_card(struct sc_profile *profile, struct sc_pkcs15_card *p15card) { struct sc_context *ctx = p15card->card->ctx; struct sc_cardctl_myeid_data_obj data_obj; struct sc_file *mf = NULL; unsigned char data[8]; int r; LOG_FUNC_CALLED(ctx); r = myeid_get_init_applet_data(profile, p15card, data, sizeof (data)); LOG_TEST_RET(ctx, r, "Get init applet date error"); /* Select parent DF and verify PINs/key as necessary */ r = sc_select_file(p15card->card, sc_get_mf_path(), &mf); LOG_TEST_RET(ctx, r, "Cannot select MF"); /* 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; data_obj.P2 = 0xE0; data_obj.Data = data; 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); } static int myeid_init_card(sc_profile_t *profile, sc_pkcs15_card_t *p15card) { struct sc_path path; struct sc_file *file = NULL; u8 rbuf[256]; int r; LOG_FUNC_CALLED(p15card->card->ctx); p15card->tokeninfo->flags = SC_PKCS15_TOKEN_PRN_GENERATION | SC_PKCS15_TOKEN_EID_COMPLIANT; _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); r = sc_select_file(p15card->card, &path, &file); sc_file_free(file); LOG_FUNC_RETURN(p15card->card->ctx, r); } /* * Create a DF */ static int myeid_create_dir(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *df) { struct sc_context *ctx = NULL; struct sc_file *file = NULL; int r = 0, ii; static const char *create_dfs[] = { "PKCS15-PrKDF", "PKCS15-PuKDF", "PKCS15-SKDF", "PKCS15-CDF", "PKCS15-CDF-TRUSTED", "PKCS15-DODF", NULL }; static const int create_dfs_val[] = { SC_PKCS15_PRKDF, SC_PKCS15_PUKDF, SC_PKCS15_SKDF, SC_PKCS15_CDF, SC_PKCS15_CDF_TRUSTED, SC_PKCS15_DODF }; if (!profile || !p15card || !p15card->card || !df) return SC_ERROR_INVALID_ARGUMENTS; ctx = p15card->card->ctx; LOG_FUNC_CALLED(ctx); sc_log(ctx, "id (%x)", df->id); if (df->id == 0x5015) { sc_log(ctx, "Select (%x)", df->id); r = sc_select_file(p15card->card, &df->path, NULL); 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) { sc_log(ctx, "Inconsistent profile: cannot find %s", create_dfs[ii]); LOG_FUNC_RETURN(ctx, SC_ERROR_INCONSISTENT_PROFILE); } r = sc_pkcs15init_add_object(p15card, profile, create_dfs_val[ii], NULL); if (r != SC_ERROR_FILE_ALREADY_EXISTS) LOG_TEST_RET(ctx, r, "Failed to create MyEID xDF file"); } } LOG_FUNC_RETURN(p15card->card->ctx, r); } /* * Select the PIN reference */ static int myeid_select_pin_reference(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_auth_info_t *auth_info) { 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 (auth_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN) { sc_log(p15card->card->ctx, "PIN_FLAG_SO_PIN, ref (%d), tries_left (%d)", auth_info->attrs.pin.reference, auth_info->tries_left); } else { sc_log(p15card->card->ctx, "PIN_FLAG_PIN, ref (%d), tries_left (%d)", auth_info->attrs.pin.reference, auth_info->tries_left); } if (auth_info->attrs.pin.reference <= 0 || auth_info->attrs.pin.reference > MYEID_MAX_PINS) auth_info->attrs.pin.reference = 1; LOG_FUNC_RETURN(p15card->card->ctx, SC_SUCCESS); } /* * Create a new PIN */ static int myeid_create_pin(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_file *df, struct sc_pkcs15_object *pin_obj, const unsigned char *pin, size_t pin_len, const unsigned char *puk, size_t puk_len) { struct sc_context *ctx = p15card->card->ctx; 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 = {0}; int r; LOG_FUNC_CALLED(ctx); sc_log(ctx, "PIN('%s',ref:%i,flags:0x%X,pin_len:%"SC_FORMAT_LEN_SIZE_T"u,puk_len:%"SC_FORMAT_LEN_SIZE_T"u)\n", pin_obj->label, auth_info->attrs.pin.reference, auth_info->attrs.pin.flags, pin_len, puk_len); if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return SC_ERROR_OBJECT_NOT_VALID; if (auth_info->attrs.pin.reference >= MYEID_MAX_PINS) return SC_ERROR_INVALID_ARGUMENTS; if (pin == NULL || puk == NULL || pin_len < 4 || puk_len < 4) return SC_ERROR_INVALID_PIN_LENGTH; sc_profile_get_pin_info(profile, (auth_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN) ? SC_PKCS15INIT_SO_PUK : SC_PKCS15INIT_USER_PUK, &puk_ainfo); memset(data, 0, sizeof (data)); /* Make command to add a pin-record */ data_obj.P1 = 0x01; data_obj.P2 = auth_info->attrs.pin.reference; /* myeid pin number */ memset(data, auth_info->attrs.pin.pad_char, 8); memcpy(&data[0], (u8 *) pin, pin_len); /* copy pin */ memset(&data[8], puk_ainfo.attrs.pin.pad_char, 8); memcpy(&data[8], (u8 *) puk, puk_len); /* copy puk */ if (auth_info->tries_left > 0 && auth_info->tries_left < 15) data[16] = auth_info->tries_left; else data[16] = 5; /* default value */ if (puk_ainfo.tries_left > 0 && puk_ainfo.tries_left < 15) data[17] = puk_ainfo.tries_left; else data[17] = 5; /* default value */ data[18] = 0x00; data_obj.Data = data; data_obj.DataLen = 19; r = sc_card_ctl(p15card->card, SC_CARDCTL_MYEID_PUTDATA, &data_obj); LOG_TEST_RET(ctx, r, "Initialize PIN failed"); LOG_FUNC_RETURN(ctx, r); } /* * Setup file struct & path: get correct template from the profile, construct full path * num = number of objects of this type already on the card */ static int myeid_new_file(sc_profile_t *profile, sc_card_t *card, unsigned int type, unsigned int num, sc_file_t **out) { sc_file_t *file; sc_path_t *p; char name[64]; const char *tag = NULL; int r; LOG_FUNC_CALLED(card->ctx); switch (type) { case SC_PKCS15_TYPE_PRKEY_RSA: case SC_PKCS15_TYPE_PRKEY_EC: tag = "private-key"; break; case SC_PKCS15_TYPE_PUBKEY_RSA: case SC_PKCS15_TYPE_PUBKEY_EC: tag = "public-key"; break; case SC_PKCS15_TYPE_SKEY_GENERIC: case SC_PKCS15_TYPE_SKEY_DES: case SC_PKCS15_TYPE_SKEY_3DES: tag = "secret-key"; break; default: if ((type & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_CERT) tag = "certificate"; else if ((type & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_DATA_OBJECT) tag = "data"; break; } if (!tag) { 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_log(card->ctx, "Profile doesn't define %s", name); return SC_ERROR_NOT_SUPPORTED; } /* Auto-increment FID for next object */ 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); /* Increment FID until there's no file with such path */ r = sc_select_file(card, p, NULL); while (r == 0) { file->id++; p->value[p->len - 2] = (u8) (file->id / 256); p->value[p->len - 1] = (u8) (file->id % 256); r = sc_select_file(card, p, NULL); } *out = file; LOG_FUNC_RETURN(card->ctx, 0); } static int myeid_encode_private_key(sc_profile_t *profile, sc_card_t *card, struct sc_pkcs15_prkey_rsa *rsa, u8 *key, size_t *keysize, int key_ref) { LOG_FUNC_CALLED(card->ctx); LOG_FUNC_RETURN(card->ctx, 0); } static int myeid_encode_public_key(sc_profile_t *profile, sc_card_t *card, struct sc_pkcs15_prkey_rsa *rsa, u8 *key, size_t *keysize, int key_ref) { LOG_FUNC_CALLED(card->ctx); LOG_FUNC_RETURN(card->ctx, 0); } /* * 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; 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; for (ii=0;iitokeninfo->supported_algos[ii].reference; ii++) if (p15card->tokeninfo->supported_algos[ii].reference >= ref) ref = p15card->tokeninfo->supported_algos[ii].reference + 1; if (ii < SC_MAX_SUPPORTED_ALGORITHMS) { algo = &p15card->tokeninfo->supported_algos[ii]; algo->reference = ref; algo->mechanism = mechanism; algo->operations = operations; algo->algo_id = *oid; profile->dirty = 1; profile->pkcs15.do_last_update = 1; } } 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) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_skey_info *skey_info = (struct sc_pkcs15_skey_info *) object->data; LOG_FUNC_CALLED(ctx); switch (object->type) { case SC_PKCS15_TYPE_SKEY_GENERIC: switch (skey_info->key_type | (skey_info->value_len << 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 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; } break; } } /* * Create a private key file */ static int myeid_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_card *card = p15card->card; struct sc_pkcs15_prkey_info *prkey_info = (struct sc_pkcs15_prkey_info *) object->data; struct sc_pkcs15_skey_info *skey_info = (struct sc_pkcs15_skey_info *) object->data; struct sc_pkcs15_id *id; struct sc_path *path; int *key_reference; struct sc_file *file = NULL; 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, pin_reference = -1; size_t keybits = 0; unsigned char prop_info[] = {0x00, 0x00}; int extractable = FALSE; LOG_FUNC_CALLED(card->ctx); switch (object->type) { case SC_PKCS15_TYPE_PRKEY_RSA: ef_structure = SC_CARDCTL_MYEID_KEY_RSA; keybits = prkey_info->modulus_length; break; case SC_PKCS15_TYPE_PRKEY_EC: ef_structure = SC_CARDCTL_MYEID_KEY_EC; keybits = prkey_info->field_length; break; case SC_PKCS15_TYPE_SKEY_DES: 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 CKK_AES: ef_structure = SC_CARDCTL_MYEID_KEY_AES; break; 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; } if (!ef_structure) { LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Unsupported key type"); } myeid_fixup_supported_algos(profile, p15card, object); if ((object->type & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_PRKEY) { id = &prkey_info->id; path = &prkey_info->path; key_reference = &prkey_info->key_reference; } else { id = &skey_info->id; path = &skey_info->path; key_reference = &skey_info->key_reference; } sc_log(ctx, "create MyEID key ID:%s", sc_pkcs15_print_id(id)); /* Get the private key file */ 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 || 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 %zu", keybits); file->size = keybits; file->ef_structure = ef_structure; memcpy(path->value, &file->path.value, file->path.len); *key_reference = file->path.value[file->path.len - 1] & 0xFF; sc_log(ctx, "Path of MyEID key file to create %s", sc_print_path(&file->path)); if (object->auth_id.len >= 1) { r = sc_pkcs15_find_pin_by_auth_id(p15card, &object->auth_id, &pin_object); if (r != SC_SUCCESS) sc_file_free(file); LOG_TEST_RET(ctx, r, "Failed to get pin object by auth_id"); if (pin_object->type != SC_PKCS15_TYPE_AUTH_PIN) { sc_file_free(file); LOG_TEST_RET(ctx, SC_ERROR_OBJECT_NOT_VALID, "Invalid object returned when locating pin object."); } pkcs15_auth_info = (struct sc_pkcs15_auth_info*) pin_object->data; if (pkcs15_auth_info == NULL || pkcs15_auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) { sc_file_free(file); LOG_TEST_RET(ctx, SC_ERROR_OBJECT_NOT_VALID, "NULL or invalid sc_pkcs15_auth_info in pin object"); } pin_reference = pkcs15_auth_info->attrs.pin.reference; if (pin_reference >= 1 && pin_reference < MYEID_MAX_PINS) { sec_attrs[0] = (pin_reference << 4 | (pin_reference & 0x0F)); sec_attrs[1] = (pin_reference << 4 | (pin_reference & 0x0F)); sc_file_set_sec_attr(file, sec_attrs, sizeof(sec_attrs)); } } else { sc_file_free(file); 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); LOG_TEST_RET(ctx, r, "Cannot create MyEID key file"); LOG_FUNC_RETURN(ctx, r); } /* * Store a private key */ static int myeid_store_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *object, struct sc_pkcs15_prkey *prkey) { struct sc_context *ctx = p15card->card->ctx; struct sc_card *card = p15card->card; struct sc_cardctl_myeid_gen_store_key_info args; struct sc_file *file = NULL; struct sc_pkcs15_id *id; struct sc_path *path; int r; LOG_FUNC_CALLED(ctx); if ((object->type & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_PRKEY) { struct sc_pkcs15_prkey_info *prkey_info = (struct sc_pkcs15_prkey_info *) object->data; id = &prkey_info->id; path = &prkey_info->path; } else { struct sc_pkcs15_skey_info *skey_info = (struct sc_pkcs15_skey_info *) object->data; id = &skey_info->id; path = &skey_info->path; } sc_log(ctx, "store MyEID key with ID:%s and path:%s", sc_pkcs15_print_id(id), sc_print_path(path)); r = sc_select_file(card, path, &file); LOG_TEST_RET(ctx, r, "Cannot store MyEID key: select key file failed"); r = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_UPDATE); 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)); args.op_type = OP_TYPE_STORE; switch (object->type) { case SC_PKCS15_TYPE_PRKEY_RSA: args.key_type = SC_CARDCTL_MYEID_KEY_RSA; args.pubexp_len = prkey->u.rsa.exponent.len; args.pubexp = prkey->u.rsa.exponent.data; args.primep_len = prkey->u.rsa.p.len; args.primep = prkey->u.rsa.p.data; args.primeq_len = prkey->u.rsa.q.len; args.primeq = prkey->u.rsa.q.data; args.dp1_len = prkey->u.rsa.dmp1.len; args.dp1 = prkey->u.rsa.dmp1.data; args.dq1_len = prkey->u.rsa.dmq1.len; args.dq1 = prkey->u.rsa.dmq1.data; args.invq_len = prkey->u.rsa.iqmp.len; args.invq = prkey->u.rsa.iqmp.data; //args.key_len_bits = keybits; args.key_len_bits = prkey->u.rsa.modulus.len; args.mod = prkey->u.rsa.modulus.data; break; case SC_PKCS15_TYPE_PRKEY_EC: args.key_type = SC_CARDCTL_MYEID_KEY_EC; args.d = prkey->u.ec.privateD.data; args.d_len = prkey->u.ec.privateD.len; args.ecpublic_point = prkey->u.ec.ecpointQ.value; args.ecpublic_point_len = prkey->u.ec.ecpointQ.len; args.key_len_bits = prkey->u.ec.params.field_length; break; case SC_PKCS15_TYPE_SKEY_GENERIC: case SC_PKCS15_TYPE_SKEY_DES: case SC_PKCS15_TYPE_SKEY_2DES: case SC_PKCS15_TYPE_SKEY_3DES: switch (prkey->algorithm) { case SC_ALGORITHM_AES: args.key_type = SC_CARDCTL_MYEID_KEY_AES; break; case SC_ALGORITHM_DES: args.key_type = SC_CARDCTL_MYEID_KEY_DES; break; } args.d = prkey->u.secret.data; args.d_len = prkey->u.secret.data_len; break; } /* Store RSA key */ r = sc_card_ctl(card, SC_CARDCTL_MYEID_GENERATE_STORE_KEY, &args); LOG_TEST_RET(ctx, r, "Card control 'MYEID_GENERATE_STORE_KEY' failed"); LOG_FUNC_RETURN(ctx, r); } static int myeid_generate_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *object, struct sc_pkcs15_pubkey *pubkey) { struct sc_context *ctx = p15card->card->ctx; struct sc_card *card = p15card->card; struct sc_pkcs15_prkey_info *key_info = (struct sc_pkcs15_prkey_info *) object->data; struct sc_cardctl_myeid_gen_store_key_info args; struct sc_file *file = NULL; int r; unsigned int cla,tag; size_t taglen; unsigned int keybits = (unsigned int)key_info->modulus_length; u8 raw_pubkey[MYEID_MAX_RSA_KEY_LEN / 8]; u8* dataptr; LOG_FUNC_CALLED(ctx); if (object->type != SC_PKCS15_TYPE_PRKEY_RSA && object->type != SC_PKCS15_TYPE_PRKEY_EC) LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Generate key failed: only RSA and EC supported"); /* Check that the card supports the requested modulus length */ switch (object->type) { case SC_PKCS15_TYPE_PRKEY_RSA: if (sc_card_find_rsa_alg(p15card->card, keybits) == NULL) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Unsupported RSA key size"); break; 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. */ if(key_info->field_length != 0) 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"); } sc_log(ctx, "Generate key with ID:%s and path:%s", sc_pkcs15_print_id(&key_info->id), sc_print_path(&key_info->path)); r = sc_select_file(card, &key_info->path, &file); 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 */ memset(&args, 0, sizeof (args)); args.key_len_bits = keybits; args.op_type = OP_TYPE_GENERATE; if (object->type == SC_PKCS15_TYPE_PRKEY_RSA) { args.key_type = SC_CARDCTL_MYEID_KEY_RSA; args.pubexp_len = MYEID_DEFAULT_PUBKEY_LEN; args.pubexp = MYEID_DEFAULT_PUBKEY; } else if (object->type == SC_PKCS15_TYPE_PRKEY_EC) { args.key_type = SC_CARDCTL_MYEID_KEY_EC; } /* 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"); /* 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.data = malloc(pubkey->u.rsa.modulus.len); pubkey->u.rsa.exponent.len = MYEID_DEFAULT_PUBKEY_LEN; pubkey->u.rsa.exponent.data = malloc(MYEID_DEFAULT_PUBKEY_LEN); 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; data_obj.P2 = 0x01; data_obj.Data = raw_pubkey; data_obj.DataLen = sizeof (raw_pubkey); r = sc_card_ctl(card, SC_CARDCTL_MYEID_GETDATA, &data_obj); LOG_TEST_RET(ctx, r, "Cannot get RSA key modulus: 'MYEID_GETDATA' failed"); if ((data_obj.DataLen * 8) != key_info->modulus_length) LOG_TEST_RET(ctx, SC_ERROR_PKCS15INIT, "Cannot get RSA key modulus: invalid key-size"); memcpy(pubkey->u.rsa.modulus.data, raw_pubkey, pubkey->u.rsa.modulus.len); } else if (object->type == SC_PKCS15_TYPE_PRKEY_EC) { struct sc_ec_parameters *ecparams = (struct sc_ec_parameters *)key_info->params.data; sc_log(ctx, "curve '%s', len %"SC_FORMAT_LEN_SIZE_T"u, oid '%s'", ecparams->named_curve, ecparams->field_length, sc_dump_oid(&(ecparams->id))); 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; data_obj.P2 = 0x86; /* Get public EC key (Q) */ data_obj.Data = raw_pubkey; data_obj.DataLen = sizeof (raw_pubkey); r = sc_card_ctl(card, SC_CARDCTL_MYEID_GETDATA, &data_obj); LOG_TEST_RET(ctx, r, "Cannot get EC public key: 'MYEID_GETDATA' failed"); 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) LOG_FUNC_RETURN(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); if (pubkey->u.ec.ecpointQ.value) free(pubkey->u.ec.ecpointQ.value); pubkey->u.ec.ecpointQ.value = malloc(taglen); if (pubkey->u.ec.ecpointQ.value == NULL) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); memcpy(pubkey->u.ec.ecpointQ.value, dataptr, taglen); pubkey->u.ec.ecpointQ.len = taglen; 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.params.der.value) 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) { 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"); } } sc_file_free(file); LOG_FUNC_RETURN(ctx, r); } /* Finish initialization. After this ACL is in affect */ static int myeid_finalize_card(sc_card_t *card) { LOG_FUNC_CALLED(card->ctx); LOG_FUNC_RETURN(card->ctx, sc_card_ctl(card, SC_CARDCTL_MYEID_ACTIVATE_CARD, NULL)); } /* * Create a new PIN */ static struct sc_pkcs15init_operations sc_pkcs15init_myeid_operations = { myeid_erase_card, myeid_init_card, /* init_card */ myeid_create_dir, /* create_dir */ NULL, /* create_domain */ myeid_select_pin_reference, myeid_create_pin, NULL, /* select_key_reference */ myeid_create_key, myeid_store_key, myeid_generate_key, myeid_encode_private_key, myeid_encode_public_key, myeid_finalize_card, myeid_delete_object, /* delete_object */ NULL, NULL, NULL, NULL, NULL, /* pkcs15init emulation */ NULL /* sanity_check */ }; struct sc_pkcs15init_operations *sc_pkcs15init_get_myeid_ops(void) { return &sc_pkcs15init_myeid_operations; } OpenSC-0.26.1/src/pkcs15init/pkcs15-oberthur-awp.c000066400000000000000000001556271474147347300214350ustar00rootroot00000000000000/* * Oberthur AWP extension for PKCS #15 initialization * * Copyright (C) 2010 Viktor Tarasov * Copyright (C) 2002 Juha Yrjola * * 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 * * best view with tabstop=4 * */ #include #include #include #include "config.h" #include "pkcs15-oberthur.h" #include "libopensc/opensc.h" #include "libopensc/cardctl.h" #include "libopensc/log.h" #include "profile.h" #include "pkcs15-init.h" #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" }; static unsigned char * awp_get_commonName(X509 *x) { unsigned char *ret = NULL; int r; 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; 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); if (ret) { memcpy(ret, a_str->data, a_str->length); *(ret + a_str->length) = '\0'; } } else { unsigned char *tmp = NULL; r = ASN1_STRING_to_UTF8(&tmp, a_str); if (r > 0) { ret = malloc(r + 1); if (ret) { memcpy(ret, tmp, r); *(ret + r) = '\0'; } OPENSSL_free(tmp); } } } return ret; } static int awp_new_file(struct sc_pkcs15_card *p15card, struct sc_profile *profile, unsigned int type, unsigned int num, struct sc_file **info_out, struct sc_file **obj_out) { struct sc_context *ctx = p15card->card->ctx; struct sc_file *ifile=NULL, *ofile=NULL; char name[NAME_MAX_LEN]; const char *itag=NULL, *otag=NULL; 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"; otag = "template-certificate"; break; case SC_PKCS15_TYPE_PRKEY_RSA: case COSM_TYPE_PRKEY_RSA: itag = "private-key-info"; otag = "template-private-key"; break; case SC_PKCS15_TYPE_PUBKEY_RSA: case COSM_TYPE_PUBKEY_RSA: itag = "public-key-info"; otag = "template-public-key"; break; case SC_PKCS15_TYPE_DATA_OBJECT: itag = "data-info"; otag = "template-data"; break; case COSM_TYPE_PRIVDATA_OBJECT: itag = "privdata-info"; otag = "template-privdata"; break; case SC_PKCS15_TYPE_AUTH_PIN: case COSM_TOKENINFO : itag = "token-info"; num = 0; break; case COSM_PUBLIC_LIST: itag = "public-list"; num = 0; break; case COSM_PRIVATE_LIST: itag = "private-list"; num = 0; break; case COSM_CONTAINER_LIST: itag = "container-list"; num = 0; break; default: return SC_ERROR_INVALID_ARGUMENTS; } if (itag) { snprintf(name, sizeof(name),"%s-%s", COSM_TITLE, itag); sc_log(ctx, "info template %s",name); if (sc_profile_get_file(profile, name, &ifile) < 0) { sc_log(ctx, "profile does not defines template '%s'", name); return SC_ERROR_INCONSISTENT_PROFILE; } } if (otag) { sc_log(ctx, "obj template %s",otag); if (sc_profile_get_file(profile, otag, &ofile) < 0) { sc_file_free(ifile); sc_log(ctx, "profile does not defines template '%s'", name); return SC_ERROR_INCONSISTENT_PROFILE; } ofile->id |= (num & 0xFF); ofile->path.value[ofile->path.len-1] |= (num & 0xFF); } if (ifile) { if(info_out) { if (ofile) { ifile->id = ofile->id | 0x100; ifile->path = ofile->path; ifile->path.value[ifile->path.len-2] |= 0x01; } 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; } else { sc_file_free(ifile); } } if (ofile) { sc_log(ctx, "obj file %04X; size %"SC_FORMAT_LEN_SIZE_T"u; ", ofile->id, ofile->size); if (obj_out) *obj_out = ofile; else sc_file_free(ofile); } LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int awp_update_blob(struct sc_context *ctx, unsigned char **blob, int *blob_size, struct awp_lv *lv, int type) { unsigned char *pp; LOG_FUNC_CALLED(ctx); switch (type) { case TLV_TYPE_LLV : if (!(pp = realloc(*blob, *blob_size + 2 + lv->len))) return SC_ERROR_OUT_OF_MEMORY; *(pp + *blob_size) = (lv->len >> 8) & 0xFF; *(pp + *blob_size + 1) = lv->len & 0xFF; memcpy(pp + *blob_size + 2, lv->value, (lv->len & 0xFF)); *blob_size += 2 + lv->len; break; case TLV_TYPE_LV : if (!(pp = realloc(*blob, *blob_size + 1 + lv->len))) return SC_ERROR_OUT_OF_MEMORY; *(pp + *blob_size) = lv->len & 0xFF; memcpy(pp + *blob_size + 1, lv->value, (lv->len & 0xFF)); *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_log(ctx, "Invalid tlv type %i",type); return SC_ERROR_INCORRECT_PARAMETERS; } *blob = pp; LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int 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]; LOG_FUNC_CALLED(ctx); if (len<0x34) LOG_TEST_RET(ctx, SC_ERROR_INCORRECT_PARAMETERS, "Invalid container update size"); rv = sc_get_challenge(p15card->card, rand_buf, sizeof(rand_buf)); LOG_TEST_RET(ctx, rv, "Cannot get challenge"); *(buff + 12) = 0x26; *(buff + 13) = '{'; for (ii=0, mm = 0; iicard->ctx; int rv; unsigned char *buff = NULL; 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) 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) { free(buff); sc_log(ctx, "Cannot create container"); LOG_FUNC_RETURN(ctx, rv); } *(buff + 0) = (acc->pubkey_id >> 8) & 0xFF; *(buff + 1) = acc->pubkey_id & 0xFF; *(buff + 2) = (acc->prkey_id >> 8) & 0xFF; *(buff + 3) = acc->prkey_id & 0xFF; *(buff + 4) = (acc->cert_id >> 8) & 0xFF; *(buff + 5) = acc->cert_id & 0xFF; rv = sc_select_file(p15card->card, &list_file->path, NULL); if (rv == SC_ERROR_FILE_NOT_FOUND) rv = sc_pkcs15init_create_file(profile, p15card, list_file); if (!rv) rv = sc_append_record(p15card->card, buff, list_file->record_length, SC_RECORD_BY_REC_NR); free(buff); LOG_FUNC_RETURN(ctx, rv); } static int awp_create_container(struct sc_pkcs15_card *p15card, struct sc_profile *profile, int type, struct awp_lv *key_id, struct awp_crypto_container *acc) { struct sc_context *ctx = p15card->card->ctx; struct sc_file *clist = NULL, *file = NULL; int rv = 0; 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); 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); LOG_TEST_RET(ctx, rv, "Create container failed: cannot select container's list"); file->record_length = clist->record_length; 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); 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, size_t rec, int offs) { struct sc_context *ctx = p15card->card->ctx; int rv; unsigned char *buff = NULL; 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_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) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); memset(buff, 0, list_file->record_length); if (rec > list_file->record_count) { rv = awp_new_container_entry(p15card, buff, list_file->record_length); } else { rv = sc_select_file(p15card->card, &list_file->path, NULL); if (!rv) 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); LOG_FUNC_RETURN(ctx, rv); } switch (type) { case SC_PKCS15_TYPE_PUBKEY_RSA: case COSM_TYPE_PUBKEY_RSA: if (*(buff + offs + 4)) 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; break; case SC_PKCS15_TYPE_PRKEY_RSA: case COSM_TYPE_PRKEY_RSA: if (*(buff + offs + 2)) { free(buff); LOG_TEST_RET(ctx, SC_ERROR_INVALID_CARD, "private key exists already"); } *(buff + offs + 2) = (file_id >> 8) & 0xFF; *(buff + offs + 3) = file_id & 0xFF; break; case SC_PKCS15_TYPE_CERT_X509 : *(buff + offs + 4) = (file_id >> 8) & 0xFF; *(buff + offs + 5) = file_id & 0xFF; break; default: free(buff); LOG_FUNC_RETURN(ctx, SC_ERROR_INCORRECT_PARAMETERS); } if (rec > list_file->record_count) { rv = sc_select_file(p15card->card, &list_file->path, NULL); if (rv == SC_ERROR_FILE_NOT_FOUND) rv = sc_pkcs15init_create_file(profile, p15card, list_file); if (!rv) rv = sc_append_record(p15card->card, buff, list_file->record_length, SC_RECORD_BY_REC_NR); } else { rv = sc_update_record(p15card->card, (unsigned int)rec, 0, buff, list_file->record_length, SC_RECORD_BY_REC_NR); } free(buff); LOG_FUNC_RETURN(ctx, rv); } static int awp_update_container(struct sc_pkcs15_card *p15card, struct sc_profile *profile, int type, struct awp_lv *key_id, unsigned obj_id, unsigned int *prkey_id) { struct sc_context *ctx = p15card->card->ctx; struct sc_file *clist = NULL, *file = NULL; struct sc_path private_path; int rv = 0; size_t rec; int rec_offs; unsigned char *list = NULL; LOG_FUNC_CALLED(ctx); sc_log(ctx, "update container(type:%X,obj_id:%X)", type, obj_id); if (prkey_id) *prkey_id = 0; /* * Get path of the DF that contains private objects. */ rv = awp_new_file(p15card, profile, SC_PKCS15_TYPE_PRKEY_RSA, 1, NULL, &file); if (rv) goto done; private_path = file->path; sc_file_free(file), file=NULL; rv = awp_new_file(p15card, profile, COSM_CONTAINER_LIST, 0, &clist, NULL); if (rv) goto done; rv = sc_select_file(p15card->card, &clist->path, &file); if (rv) goto done; file->record_length = clist->record_length; if (type == SC_PKCS15_TYPE_PRKEY_RSA || type == COSM_TYPE_PRKEY_RSA) { rec_offs = 0; rv = awp_update_container_entry(p15card, profile, file, type, obj_id, file->record_count + 1, rec_offs); goto done; } list = malloc(AWP_CONTAINER_RECORD_LEN * file->record_count); if (!list) { rv = SC_ERROR_OUT_OF_MEMORY; goto done; } rv = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_READ); if (rv) goto done; for (rec=0; rec < file->record_count; rec++) { unsigned char tmp[256]; 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 goto done; } for (rec=0, rv=0; !rv && rec < file->record_count; rec++) { for (rec_offs=0; !rv && rec_offs<12; rec_offs+=6) { int 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; struct sc_path path = private_path; struct sc_file *ff = NULL; path.value[path.len - 2] = *(list + offs + 2) | 0x01; path.value[path.len - 1] = *(list + offs + 3); rv = sc_select_file(p15card->card, &path, &ff); if (rv) continue; rv = sc_pkcs15init_authenticate(profile, p15card, ff, SC_AC_OP_READ); if (rv) { sc_file_free(ff); break; } buff = malloc(ff->size); if (!buff) rv = SC_ERROR_OUT_OF_MEMORY; if (!rv) { rv = sc_read_binary(p15card->card, 0, buff, ff->size, 0); if ((unsigned)rv == ff->size) { rv = 0; id_offs = 5 + *(buff+3); if (key_id->len == *(buff + id_offs) && !memcmp(key_id->value, buff + id_offs + 1, key_id->len)) { sc_log(ctx, "found key file friend"); if (!rv) rv = awp_update_container_entry(p15card, profile, file, type, obj_id, rec + 1, rec_offs); if (rv >= 0 && prkey_id) *prkey_id = *(list + offs + 2) * 0x100 + *(list + offs + 3); } } } free(buff); sc_file_free(ff); if (rv) break; } } } done: sc_file_free(clist); sc_file_free(file); if (list) free(list); LOG_FUNC_RETURN(ctx, rv); } static int awp_update_df_create_pin(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15_object *pinobj) { SC_FUNC_CALLED(p15card->card->ctx, 1); /* No update DF when creating PIN objects */ SC_FUNC_RETURN(p15card->card->ctx, 1, SC_SUCCESS); } static int awp_set_certificate_info (struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_file *file, struct awp_cert_info *ci) { struct sc_context *ctx = p15card->card->ctx; int r = 0, blob_size; unsigned char *blob; const char *default_cert_label = "Certificate"; LOG_FUNC_CALLED(ctx); blob_size = 2; if (!(blob = malloc(blob_size))) { r = SC_ERROR_OUT_OF_MEMORY; goto done; } /* TODO: cert flags */ *blob = (COSM_TAG_CERT >> 8) & 0xFF; *(blob + 1) = COSM_TAG_CERT & 0xFF; if (ci->label.len && ci->label.len != strlen(default_cert_label) && memcmp(ci->label.value, default_cert_label, strlen(default_cert_label))) r = awp_update_blob(ctx, &blob, &blob_size, &ci->label, TLV_TYPE_LLV); else r = awp_update_blob(ctx, &blob, &blob_size, &ci->cn, TLV_TYPE_LLV); if (r) goto done; r = awp_update_blob(ctx, &blob, &blob_size, &ci->id, TLV_TYPE_LLV); if (r) goto done; r = awp_update_blob(ctx, &blob, &blob_size, &ci->subject, TLV_TYPE_LLV); if (r) goto done; if (ci->issuer.len != ci->subject.len || memcmp(ci->issuer.value, ci->subject.value, ci->subject.len)) { r = awp_update_blob(ctx, &blob, &blob_size, &ci->issuer, TLV_TYPE_LLV); if (r) goto done; r = awp_update_blob(ctx, &blob, &blob_size, &ci->serial, TLV_TYPE_LLV); if (r) goto done; } else { r = awp_update_blob(ctx, &blob, &blob_size, &zero_lv, TLV_TYPE_LLV); if (r) goto done; r = awp_update_blob(ctx, &blob, &blob_size, &zero_lv, TLV_TYPE_LLV); if (r) goto done; } file->size = blob_size; r = sc_pkcs15init_create_file(profile, p15card, file); if (r) goto done; r = sc_pkcs15init_update_file(profile, p15card, file, blob, blob_size); if (r < 0) goto done; r = 0; done: if (blob) free(blob); LOG_FUNC_RETURN(ctx, r); } static int awp_update_object_list(struct sc_pkcs15_card *p15card, struct sc_profile *profile, unsigned int type, int num) { struct sc_context *ctx = p15card->card->ctx; struct sc_file *obj_file = NULL, *lst_file = NULL; struct sc_file *file = NULL; char obj_name[NAME_MAX_LEN], lst_name[NAME_MAX_LEN]; unsigned char *buff = NULL; int rv; unsigned ii; unsigned long flags; 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"); snprintf(lst_name, NAME_MAX_LEN,"%s-public-list", COSM_TITLE); break; case SC_PKCS15_TYPE_PUBKEY_RSA: case COSM_TYPE_PUBKEY_RSA: snprintf(obj_name, NAME_MAX_LEN, "template-public-key"); snprintf(lst_name, NAME_MAX_LEN,"%s-public-list", COSM_TITLE); break; case SC_PKCS15_TYPE_DATA_OBJECT: snprintf(obj_name, NAME_MAX_LEN, "template-data"); snprintf(lst_name, NAME_MAX_LEN,"%s-public-list", COSM_TITLE); break; case COSM_TYPE_PRIVDATA_OBJECT: snprintf(obj_name, NAME_MAX_LEN, "template-privdata"); snprintf(lst_name, NAME_MAX_LEN,"%s-private-list", COSM_TITLE); break; case SC_PKCS15_TYPE_PRKEY_RSA: case COSM_TYPE_PRKEY_RSA: snprintf(obj_name, NAME_MAX_LEN,"template-private-key"); snprintf(lst_name, NAME_MAX_LEN,"%s-private-list", COSM_TITLE); break; default: sc_log(ctx, "Not supported file type %X", type); return SC_ERROR_INVALID_ARGUMENTS; } 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_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_log(ctx, "No profile template '%s'", lst_name); rv = SC_ERROR_NOT_SUPPORTED; goto done; } obj_file->id |= (num & 0xFF); obj_file->path.value[obj_file->path.len-1] |= (num & 0xFF); rv = sc_select_file(p15card->card, &obj_file->path, &file); if (rv) goto done; if (type == SC_PKCS15_TYPE_PUBKEY_RSA || type == COSM_TYPE_PUBKEY_RSA) { if (file->size==PUBKEY_512_ASN1_SIZE) file->size = 512; else if (file->size==PUBKEY_1024_ASN1_SIZE) file->size = 1024; else if (file->size==PUBKEY_2048_ASN1_SIZE) file->size = 2048; } buff = malloc(lst_file->size); if (!buff) { rv = SC_ERROR_OUT_OF_MEMORY; goto done; } rv = sc_pkcs15init_authenticate(profile, p15card, lst_file, SC_AC_OP_READ); if (rv) goto done; rv = sc_pkcs15init_authenticate(profile, p15card, lst_file, SC_AC_OP_UPDATE); if (rv) goto done; rv = sc_select_file(p15card->card, &lst_file->path, NULL); if (rv == SC_ERROR_FILE_NOT_FOUND) rv = sc_pkcs15init_create_file(profile, p15card, lst_file); if (rv < 0) 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) if (*(buff + ii) != COSM_LIST_TAG) break; if (ii>=lst_file->size) { rv = SC_ERROR_UNKNOWN_DATA_RECEIVED; goto done; } 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; *(buff + ii + 1) = (file->id >> 8) & 0xFF; *(buff + ii + 2) = file->id & 0xFF; *(buff + ii + 3) = (file->size >> 8) & 0xFF; *(buff + ii + 4) = file->size & 0xFF; rv = sc_update_binary(p15card->card, ii, buff + ii, 5, 0); sc_log(ctx, "rv %i",rv); if (rv < 0) goto done; rv = 0; done: if (buff) free(buff); sc_file_free(lst_file); sc_file_free(obj_file); sc_file_free(file); LOG_FUNC_RETURN(ctx, rv); } static int awp_encode_key_info(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *obj, struct sc_pkcs15_pubkey_rsa *pubkey, struct awp_key_info *ki) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_prkey_info *key_info; int r = 0; LOG_FUNC_CALLED(ctx); key_info = (struct sc_pkcs15_prkey_info *)obj->data; 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) ki->flags = COSM_TAG_PRVKEY_RSA; else return SC_ERROR_INCORRECT_PARAMETERS; if (obj->type == COSM_TYPE_PUBKEY_RSA || obj->type == COSM_TYPE_PRKEY_RSA) ki->flags |= COSM_GENERATED; ki->label.value = (unsigned char *)strdup(obj->label); ki->label.len = strlen(obj->label); 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_log(ctx, "pubkey->modulus.len %"SC_FORMAT_LEN_SIZE_T"u", pubkey->modulus.len); ki->modulus.value = malloc(pubkey->modulus.len); if (!ki->modulus.value) { r = SC_ERROR_OUT_OF_MEMORY; goto done; } memcpy(ki->modulus.value, pubkey->modulus.data, pubkey->modulus.len); ki->modulus.len = pubkey->modulus.len; /* * Oberthur saves exponents as length and value, without tag. */ ki->exponent.value = malloc(pubkey->exponent.len); if (!ki->exponent.value) { r = SC_ERROR_OUT_OF_MEMORY; goto done; } memcpy(ki->exponent.value, pubkey->exponent.data, pubkey->exponent.len); ki->exponent.len = pubkey->exponent.len; /* * ID */ ki->id.value = calloc(1, key_info->id.len); if (!ki->id.value) 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_log(ctx, "cosm_encode_key_info() label:%s",ki->label.value); done: LOG_FUNC_RETURN(ctx, r); } static void awp_free_key_info(struct awp_key_info *ki) { free(ki->modulus.value); free(ki->exponent.value); free(ki->id.value); } static int awp_set_key_info (struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_file *file, struct awp_key_info *ki, struct awp_cert_info *ci) { struct sc_context *ctx = p15card->card->ctx; int r = 0, blob_size; unsigned char *blob; 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) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "AWP set key info failed: blob allocation error"); sc_log(ctx, "label:%s",ki->label.value); *blob = (ki->flags >> 8) & 0xFF; *(blob + 1) = ki->flags & 0xFF; if (ci && ci->label.len) r = awp_update_blob(ctx, &blob, &blob_size, &ci->label, TLV_TYPE_LLV); else if (ci && !ci->label.len) r = awp_update_blob(ctx, &blob, &blob_size, &ci->cn, TLV_TYPE_LLV); else r = awp_update_blob(ctx, &blob, &blob_size, &ki->label, TLV_TYPE_LLV); if (r) goto done; r = awp_update_blob(ctx, &blob, &blob_size, &ki->id, TLV_TYPE_LLV); if (r) goto done; r = awp_update_blob(ctx, &blob, &blob_size, &x30_lv, TLV_TYPE_V); if (r) goto done; if (ci) r = awp_update_blob(ctx, &blob, &blob_size, &(ci->subject), TLV_TYPE_LLV); else r = awp_update_blob(ctx, &blob, &blob_size, &zero_lv, TLV_TYPE_LLV); if (r) goto done; if ((ki->flags & ~COSM_GENERATED) != COSM_TAG_PUBKEY_RSA) { r = awp_update_blob(ctx, &blob, &blob_size, &ki->modulus, TLV_TYPE_V); if (r) goto done; r = awp_update_blob(ctx, &blob, &blob_size, &ki->exponent, TLV_TYPE_LV); if (r) goto done; } file->size = blob_size; r = sc_pkcs15init_create_file(profile, p15card, file); if (r == SC_ERROR_FILE_ALREADY_EXISTS) { r = cosm_delete_file(p15card, profile, file); if (!r) r = sc_pkcs15init_create_file(profile, p15card, file); } if (r<0) goto done; r = sc_pkcs15init_update_file(profile, p15card, file, blob, blob_size); if (r < 0) goto done; r = 0; done: if (blob) free(blob); LOG_FUNC_RETURN(ctx, r); } static int awp_encode_cert_info(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *obj, struct awp_cert_info *ci) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_cert_info *cert_info; struct sc_pkcs15_pubkey_rsa pubkey; int r = 0; unsigned char *buff = NULL, *ptr; BIO *mem = NULL; X509 *x = NULL; LOG_FUNC_CALLED(ctx); if (!obj || !ci) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "AWP encode cert failed: invalid parameters"); cert_info = (struct sc_pkcs15_cert_info *)obj->data; 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); memset(&pubkey, 0, sizeof(pubkey)); ci->label.value = (unsigned char *)strdup(obj->label); ci->label.len = strlen(obj->label); 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_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) 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) { 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); /* * subject DN */ ptr = buff; r = i2d_X509_NAME(X509_get_subject_name(x),&ptr); 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) { 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; /* * issuer DN */ ptr = buff; r = i2d_X509_NAME(X509_get_issuer_name(x),&ptr); 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) { 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; /* * ID */ ci->id.value = calloc(1, cert_info->id.len); 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; /* * 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 * 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 simplify the code and make compatible with OpenSSL 1.1. This needs to be tested */ ci->serial.len = 0; ci->serial.value = NULL; /* get length */ ci->serial.len = i2d_ASN1_INTEGER(X509_get_serialNumber(x), NULL); if (ci->serial.len > 0) { if (!(ci->serial.value = malloc(ci->serial.len))) { ci->serial.len = 0; r = SC_ERROR_OUT_OF_MEMORY; 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_log(ctx, "cert. serial encoded length %zu", ci->serial.len); #else do { int encoded_len; unsigned char encoded[0x40], *encoded_ptr; encoded_ptr = encoded; encoded_len = i2c_ASN1_INTEGER(X509_get_serialNumber(x), &encoded_ptr); if (!(ci->serial.value = malloc(encoded_len + 3))) { r = SC_ERROR_OUT_OF_MEMORY; goto err; } memcpy(ci->serial.value + 2, encoded, encoded_len); *(ci->serial.value + 0) = V_ASN1_INTEGER; *(ci->serial.value + 1) = encoded_len; ci->serial.len = encoded_len + 2; sc_log(ctx, "cert. serial encoded length %i", encoded_len); } while (0); #endif ci->x509 = X509_dup(x); err: ERR_print_errors_fp(stderr); ERR_clear_error(); if (pubkey.exponent.data) free(pubkey.exponent.data); if (pubkey.modulus.data) free(pubkey.modulus.data); if (x) X509_free(x); if (mem) BIO_free(mem); if (buff) OPENSSL_free(buff); LOG_FUNC_RETURN(ctx, r); } static void awp_free_cert_info(struct awp_cert_info *ci) { 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->subject.len && ci->subject.value) free(ci->subject.value); if (ci->issuer.len && ci->issuer.value) free(ci->issuer.value); if (ci->x509) X509_free(ci->x509); memset(ci,0,sizeof(struct awp_cert_info)); } } static int awp_encode_data_info(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *obj, struct awp_data_info *di) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_data_info *data_info; int r = 0; unsigned char *buf = NULL; size_t buflen; LOG_FUNC_CALLED(ctx); if (!obj || !di) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "AWP encode data failed: invalid parameters"); data_info = (struct sc_pkcs15_data_info *)obj->data; 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); di->flags = 0x0000; di->label.value = (unsigned char *)strdup(obj->label); di->label.len = strlen(obj->label); di->app.len = strlen(data_info->app_label); if (di->app.len) { di->app.value = (unsigned char *)strdup(data_info->app_label); if (!di->app.value) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); } r = sc_asn1_encode_object_id(&buf, &buflen, &data_info->app_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); LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "AWP encode data failed: cannot allocate OID"); } *(di->oid.value + 0) = 0x06; *(di->oid.value + 1) = buflen; memcpy(di->oid.value + 2, buf, buflen); free(buf); LOG_FUNC_RETURN(ctx, r); } static void awp_free_data_info(struct awp_data_info *di) { if (di->label.len && di->label.value) free(di->label.value); if (di->app.len && di->app.value) free(di->app.value); if (di->oid.len && di->oid.value) free(di->oid.value); memset(di, 0, sizeof(struct awp_data_info)); } static int awp_set_data_info (struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_file *file, struct awp_data_info *di) { struct sc_context *ctx = p15card->card->ctx; int r = 0, blob_size; unsigned char *blob; 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; goto done; } *blob = (di->flags >> 8) & 0xFF; *(blob + 1) = di->flags & 0xFF; r = awp_update_blob(ctx, &blob, &blob_size, &di->label, TLV_TYPE_LLV); if (r) goto done; r = awp_update_blob(ctx, &blob, &blob_size, &di->app, TLV_TYPE_LLV); if (r) goto done; r = awp_update_blob(ctx, &blob, &blob_size, &di->oid, TLV_TYPE_LLV); if (r) goto done; file->size = blob_size; r = sc_pkcs15init_create_file(profile, p15card, file); if (r) goto done; r = sc_pkcs15init_update_file(profile, p15card, file, blob, blob_size); if (r < 0) goto done; r = 0; done: if (blob) free(blob); LOG_FUNC_RETURN(ctx, r); } static int awp_get_lv(struct sc_context *ctx, unsigned char *buf, size_t buf_len, size_t offs, int len_len, struct awp_lv *out) { int len = 0, ii; if (buf_len - offs < 2) return 0; if (len_len > 2) { len = len_len; len_len = 0; } else { for (len=0, ii=0; iivalue) free(out->value); out->value = malloc(len); if (!out->value) return SC_ERROR_OUT_OF_MEMORY; memcpy(out->value, buf + offs + len_len, len); out->len = len; } return len_len + len; } static int awp_parse_key_info(struct sc_context *ctx, unsigned char *buf, size_t buf_len, struct awp_key_info *ikey) { size_t offs; int len; LOG_FUNC_CALLED(ctx); offs = 0; /* Flags */ if (buf_len - offs < 2) 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); LOG_TEST_RET(ctx, len, "AWP parse key info failed: label"); if (!len) LOG_FUNC_RETURN(ctx, SC_SUCCESS); offs += len; /* Ignore Key ID */ len = awp_get_lv(ctx, buf, buf_len, offs, 2, &ikey->id); LOG_TEST_RET(ctx, len, "AWP parse key info failed: ID"); if (!len) LOG_FUNC_RETURN(ctx, SC_SUCCESS); offs += len; while (*(buf + offs) == '0') offs++; /* Subject */ len = awp_get_lv(ctx, buf, buf_len, offs, 2, &ikey->subject); LOG_TEST_RET(ctx, len, "AWP parse key info failed: subject"); if (!len) LOG_FUNC_RETURN(ctx, SC_SUCCESS); offs += len; /* Modulus */ if (buf_len - offs > 64 && buf_len - offs < 128) len = awp_get_lv(ctx, buf, buf_len, offs, 64, &ikey->modulus); else if (buf_len - offs > 128 && buf_len - offs < 256) 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); LOG_TEST_RET(ctx, len, "AWP parse key info failed: modulus"); if (!len) LOG_FUNC_RETURN(ctx, SC_SUCCESS); offs += len; /* Exponent */ len = awp_get_lv(ctx, buf, buf_len, offs, 1, &ikey->exponent); LOG_TEST_RET(ctx, len, "AWP parse key info failed: exponent"); if (!len) LOG_FUNC_RETURN(ctx, SC_SUCCESS); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int awp_update_key_info(struct sc_pkcs15_card *p15card, struct sc_profile *profile, unsigned prvkey_id, struct awp_cert_info *ci) { struct sc_context *ctx = p15card->card->ctx; struct sc_file *key_file=NULL, *info_file=NULL, *file=NULL; struct awp_key_info ikey; int rv = 0; unsigned char *buf; size_t buf_len; LOG_FUNC_CALLED(ctx); rv = awp_new_file(p15card, profile, SC_PKCS15_TYPE_PRKEY_RSA, prvkey_id & 0xFF, &info_file, &key_file); 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_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_log(ctx, "AWP update key info failed: cannot select info file"); goto done; } buf = calloc(1,file->size); if (!buf) 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_log(ctx, "AWP update key info failed: read info file error"); goto done; } buf_len = rv; memset(&ikey, 0, sizeof(ikey)); rv = awp_parse_key_info(ctx, buf, buf_len, &ikey); if (rv < 0) { 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); 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); LOG_FUNC_RETURN(ctx, rv); } static int awp_update_df_create_cert(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15_object *obj) { struct sc_context *ctx = p15card->card->ctx; struct sc_file *info_file=NULL, *obj_file=NULL; struct awp_cert_info icert; struct sc_pkcs15_der der; struct sc_path path; unsigned prvkey_id, obj_id; int rv; 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); LOG_TEST_RET(ctx, rv, "COSM new file error"); memset(&icert, 0, sizeof(icert)); 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_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_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_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_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE, rv, "'Create Cert' update DF failed: cannot update container"); sc_log(ctx, "PrvKeyID:%04X", prvkey_id); if (prvkey_id) rv = awp_update_key_info(p15card, profile, prvkey_id, &icert); 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); LOG_FUNC_RETURN(ctx, rv); } static int awp_update_df_create_prvkey(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15_object *key_obj) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_pubkey pubkey; struct sc_pkcs15_der der; struct awp_key_info ikey; struct awp_cert_info icert; struct sc_file *info_file=NULL; struct sc_pkcs15_prkey_info *key_info; struct sc_pkcs15_object *cert_obj = NULL, *pubkey_obj = NULL; struct sc_path path; struct awp_crypto_container cc; struct sc_pkcs15_cert *p15cert = NULL; int rv; 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; memset(&cc, 0, sizeof(cc)); path = key_info->path; cc.prkey_id = (path.value[path.len-1] & 0xFF) + (path.value[path.len-2] & 0xFF) * 0x100; 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, 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_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_VERBOSE, rv, "AWP 'update private key' DF failed: cannot encode cert info"); sc_pkcs15_free_certificate(p15cert); p15cert = NULL; } rv = sc_pkcs15_find_pubkey_by_id(p15card, &key_info->id, &pubkey_obj); if (!rv) { path = ((struct sc_pkcs15_cert_info *)pubkey_obj->data)->path; cc.pubkey_id = (path.value[path.len-1] & 0xFF) + (path.value[path.len-2] & 0xFF) * 0x100; } rv = awp_new_file(p15card, profile, key_obj->type, cc.prkey_id & 0xFF, &info_file, NULL); SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE, rv, "New private key info file error"); pubkey.algorithm = SC_ALGORITHM_RSA; 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_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_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_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_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_VERBOSE, rv, "AWP 'update private key' DF failed: update container error"); err: if (p15cert) sc_pkcs15_free_certificate(p15cert); sc_file_free(info_file); if (cert_obj) awp_free_cert_info(&icert); awp_free_key_info(&ikey); LOG_FUNC_RETURN(ctx, rv); } static int awp_update_df_create_pubkey(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15_object *obj) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_pubkey pubkey; struct sc_pkcs15_der der; struct awp_key_info ikey; struct sc_file *info_file=NULL; struct sc_path path; unsigned obj_id; int index, rv; 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_VERBOSE, rv, "New public key info file error"); pubkey.algorithm = SC_ALGORITHM_RSA; 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_VERBOSE, rv, "AWP 'update public key' DF failed: decode public key error"); rv = awp_encode_key_info(p15card, obj, &pubkey.u.rsa, &ikey); 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_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_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_VERBOSE, rv, "AWP 'update public key' DF failed: update container error"); err: awp_free_key_info(&ikey); sc_file_free(info_file); LOG_FUNC_RETURN(ctx, rv); } static int awp_update_df_create_data(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15_object *obj) { struct sc_context *ctx = p15card->card->ctx; struct sc_file *info_file=NULL, *obj_file=NULL; struct awp_data_info idata; struct sc_path path; unsigned obj_id, obj_type = obj->auth_id.len ? COSM_TYPE_PRIVDATA_OBJECT : SC_PKCS15_TYPE_DATA_OBJECT; int rv; LOG_FUNC_CALLED(ctx); memset(&idata, 0, sizeof(idata)); 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); LOG_TEST_GOTO_ERR(ctx, rv, "COSM new file error"); rv = awp_encode_data_info(p15card, obj, &idata); LOG_TEST_GOTO_ERR(ctx, rv, "'Create Data' update DF failed: cannot encode info"); rv = awp_set_data_info(p15card, profile, info_file, &idata); 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); 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); LOG_FUNC_RETURN(ctx, rv); } int awp_update_df_create(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15_object *object) { struct sc_context *ctx = p15card->card->ctx; int rv = SC_ERROR_INTERNAL; LOG_FUNC_CALLED(ctx); if (!object) LOG_FUNC_RETURN(ctx, SC_SUCCESS); switch (object->type) { case SC_PKCS15_TYPE_AUTH_PIN: rv = awp_update_df_create_pin(p15card, profile, object); break; case SC_PKCS15_TYPE_CERT_X509: rv = awp_update_df_create_cert(p15card, profile, object); break; case SC_PKCS15_TYPE_PRKEY_RSA: rv = awp_update_df_create_prvkey(p15card, profile, object); break; case SC_PKCS15_TYPE_PUBKEY_RSA: rv = awp_update_df_create_pubkey(p15card, profile, object); break; case SC_PKCS15_TYPE_DATA_OBJECT: rv = awp_update_df_create_data(p15card, profile, object); break; default: LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "'Create' update DF failed: unsupported object type"); } LOG_FUNC_RETURN(ctx, rv); } static int awp_delete_from_container(struct sc_pkcs15_card *p15card, struct sc_profile *profile, int type, int file_id) { struct sc_context *ctx = p15card->card->ctx; struct sc_file *clist=NULL, *file=NULL; unsigned rec, rec_len; int rv = 0, ii; unsigned char *buff=NULL; 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); LOG_TEST_RET(ctx, rv, "AWP update container entry: cannot get allocate AWP file"); rv = sc_select_file(p15card->card, &clist->path, &file); LOG_TEST_RET(ctx, rv, "AWP update container entry: cannot select container list file"); buff = malloc(file->record_length); if (!buff) 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, 0, buff, file->record_length, SC_RECORD_BY_REC_NR); if (rv < 0) { sc_log(ctx, "AWP update container entry: read record error %i", rv); break; } rec_len = rv; for (ii=0; ii<12; ii+=2) if (file_id == (*(buff+ii) * 0x100 + *(buff+ii+1))) break; if (ii==12) continue; if (type == SC_PKCS15_TYPE_PRKEY_RSA || type == COSM_TYPE_PRKEY_RSA) memset(buff + ii/6*6, 0, 6); else memset(buff + ii, 0, 2); 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_log(ctx, "AWP update container entry: 'erase' authentication error %i", rv); break; } rv = sc_delete_record(p15card->card, rec); if (rv < 0) { 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_log(ctx, "AWP update container entry: 'update' authentication error %i", rv); break; } rv = sc_update_record(p15card->card, rec, 0, buff, rec_len, SC_RECORD_BY_REC_NR); if (rv < 0) { sc_log(ctx, "AWP update container entry: update record error %i", rv); break; } } } if (rv > 0) rv = 0; free(buff); sc_file_free(clist); sc_file_free(file); LOG_FUNC_RETURN(ctx, rv); } static int awp_remove_from_object_list( struct sc_pkcs15_card *p15card, struct sc_profile *profile, int type, unsigned int obj_id) { struct sc_context *ctx = p15card->card->ctx; struct sc_file *lst_file=NULL, *lst=NULL; int rv = 0; unsigned ii; char lst_name[NAME_MAX_LEN]; unsigned char *buff=NULL; unsigned char id[2]; LOG_FUNC_CALLED(ctx); sc_log(ctx, "type %X; obj_id %X",type, obj_id); switch (type) { case SC_PKCS15_TYPE_PRKEY_RSA: case COSM_TYPE_PRKEY_RSA: snprintf(lst_name, NAME_MAX_LEN,"%s-private-list", COSM_TITLE); break; case SC_PKCS15_TYPE_PUBKEY_RSA: case SC_PKCS15_TYPE_CERT_X509: case SC_PKCS15_TYPE_DATA_OBJECT: case COSM_TYPE_PUBKEY_RSA: snprintf(lst_name, NAME_MAX_LEN,"%s-public-list", COSM_TITLE); break; default: LOG_TEST_RET(ctx, SC_ERROR_INCORRECT_PARAMETERS, "AWP update object list: invalid type"); } sc_log(ctx, "AWP update object list: select '%s' file", lst_name); rv = sc_profile_get_file(profile, lst_name, &lst_file); LOG_TEST_RET(ctx, rv, "AWP update object list: cannot instantiate list file"); rv = sc_select_file(p15card->card, &lst_file->path, &lst); LOG_TEST_RET(ctx, rv, "AWP update object list: cannot select list file"); rv = sc_pkcs15init_authenticate(profile, p15card, lst, SC_AC_OP_READ); LOG_TEST_RET(ctx, rv, "AWP update object list: 'read' authentication failed"); buff = malloc(lst->size); if (!buff) 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) goto done; id[0] = (obj_id >> 8) & 0xFF; id[1] = obj_id & 0xFF; for (ii=0; iisize; ii+=5) { if (*(buff+ii)==0xFF && *(buff+ii+1)==id[0] && *(buff+ii+2)==id[1]) { rv = sc_pkcs15init_authenticate(profile, p15card, lst, SC_AC_OP_UPDATE); if (rv) goto done; rv = sc_update_binary(p15card->card, ii, (unsigned char *)"\0", 1, 0); if (rv && rv!=1) rv = SC_ERROR_INVALID_CARD; break; } } if (rv > 0) rv = 0; done: if (buff) free(buff); sc_file_free(lst); sc_file_free(lst_file); LOG_FUNC_RETURN(ctx, rv); } static int awp_update_df_delete_cert(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15_object *obj) { struct sc_context *ctx = p15card->card->ctx; struct sc_file *info_file = NULL; struct sc_path path; int rv = SC_ERROR_NOT_SUPPORTED; unsigned file_id; 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_log(ctx, "file-id:%X", file_id); rv = awp_new_file(p15card, profile, obj->type, file_id & 0xFF, &info_file, NULL); 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) 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); 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); LOG_TEST_RET(ctx, rv, "AWP 'delete cert' update DF failed: cannot remove object"); LOG_FUNC_RETURN(ctx, rv); } static int awp_update_df_delete_prvkey(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15_object *obj) { struct sc_context *ctx = p15card->card->ctx; struct sc_file *info_file = NULL; struct sc_path path; int rv = SC_ERROR_NOT_SUPPORTED; unsigned file_id; 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_log(ctx, "file-id:%X", file_id); rv = awp_new_file(p15card, profile, obj->type, file_id & 0xFF, &info_file, NULL); 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) 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); 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); LOG_TEST_RET(ctx, rv, "AWP 'delete prkey' update DF failed: cannot remove object"); LOG_FUNC_RETURN(ctx, rv); } static int awp_update_df_delete_pubkey(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15_object *obj) { struct sc_context *ctx = p15card->card->ctx; struct sc_file *info_file = NULL; struct sc_path path; int rv = SC_ERROR_NOT_SUPPORTED; unsigned file_id; 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_log(ctx, "file-id:%X", file_id); rv = awp_new_file(p15card, profile, obj->type, file_id & 0xFF, &info_file, NULL); 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) 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); 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); LOG_TEST_RET(ctx, rv, "AWP 'delete pubkey' update DF failed: cannot remove object"); LOG_FUNC_RETURN(ctx, rv); } static int awp_update_df_delete_data(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15_object *obj) { struct sc_context *ctx = p15card->card->ctx; struct sc_file *info_file = NULL; struct sc_path path; int rv = SC_ERROR_NOT_SUPPORTED; unsigned file_id; 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_log(ctx, "file-id:%X", file_id); rv = awp_new_file(p15card, profile, obj->type, file_id & 0xFF, &info_file, NULL); 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) 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); LOG_TEST_RET(ctx, rv, "AWP 'delete DATA' update DF failed: cannot remove object"); LOG_FUNC_RETURN(ctx, rv); } int awp_update_df_delete(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15_object *object) { struct sc_context *ctx = p15card->card->ctx; int rv = SC_ERROR_INTERNAL; LOG_FUNC_CALLED(ctx); if (!object) LOG_FUNC_RETURN(ctx, SC_SUCCESS); switch (object->type) { case SC_PKCS15_TYPE_CERT_X509: rv = awp_update_df_delete_cert(p15card, profile, object); break; case SC_PKCS15_TYPE_PRKEY_RSA: rv = awp_update_df_delete_prvkey(p15card, profile, object); break; case SC_PKCS15_TYPE_PUBKEY_RSA: rv = awp_update_df_delete_pubkey(p15card, profile, object); break; case SC_PKCS15_TYPE_DATA_OBJECT: rv = awp_update_df_delete_data(p15card, profile, object); break; default: LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "'Create' update DF failed: unsupported object type"); } SC_FUNC_RETURN(ctx, 1, rv); } #endif /* #ifdef ENABLE_OPENSSL */ OpenSC-0.26.1/src/pkcs15init/pkcs15-oberthur.c000066400000000000000000000704251474147347300206400ustar00rootroot00000000000000/* * Oberthur specific operation for PKCS #15 initialization * * Copyright (C) 2002 Juha Yrjölä * Copyright (C) 2009 Viktor Tarasov , * OpenTrust * * 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 "pkcs15-oberthur.h" #include #include #include "libopensc/opensc.h" #include "libopensc/cardctl.h" #include "libopensc/log.h" #include "profile.h" #include "pkcs15-init.h" #define COSM_TITLE "OberthurAWP" #define TLV_TYPE_V 0 #define TLV_TYPE_LV 1 #define TLV_TYPE_TLV 2 /* Should be greater then SC_PKCS15_TYPE_CLASS_MASK */ #define SC_DEVICE_SPECIFIC_TYPE 0x1000 #define COSM_TYPE_PRKEY_RSA (SC_DEVICE_SPECIFIC_TYPE | SC_PKCS15_TYPE_PRKEY_RSA) #define COSM_TYPE_PUBKEY_RSA (SC_DEVICE_SPECIFIC_TYPE | SC_PKCS15_TYPE_PUBKEY_RSA) #define COSM_TOKEN_FLAG_PRN_GENERATION 0x01 #define COSM_TOKEN_FLAG_LOGIN_REQUIRED 0x04 #define COSM_TOKEN_FLAG_USER_PIN_INITIALIZED 0x08 #define COSM_TOKEN_FLAG_TOKEN_INITIALIZED 0x0400 static int cosm_create_reference_data(struct sc_profile *, struct sc_pkcs15_card *, struct sc_pkcs15_auth_info *, const unsigned char *, size_t, const unsigned char *, size_t); static int cosm_update_pin(struct sc_profile *, struct sc_pkcs15_card *, struct sc_pkcs15_auth_info *, const unsigned char *, size_t, const unsigned char *, size_t); static int cosm_write_tokeninfo (struct sc_pkcs15_card *p15card, struct sc_profile *profile, char *label, unsigned flags) { struct sc_context *ctx; struct sc_file *file = NULL; int rv; size_t sz; char *buffer = NULL; if (!p15card || !p15card->card || !profile) return SC_ERROR_INVALID_ARGUMENTS; ctx = p15card->card->ctx; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); if (sc_profile_get_file(profile, COSM_TITLE"-token-info", &file)) { rv = SC_ERROR_INCONSISTENT_PROFILE; 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_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_VERBOSE, rv, "Allocation error in cosm_write_tokeninfo()"); } if (label) strncpy(buffer, label, file->size - 4); else if (p15card->tokeninfo->label) snprintf(buffer, file->size - 4, "%s", p15card->tokeninfo->label); else if (profile->p15_spec && profile->p15_spec->tokeninfo->label) snprintf(buffer, file->size - 4, "%s", profile->p15_spec->tokeninfo->label); else snprintf(buffer, file->size - 4, "OpenSC-Token"); sz = strlen(buffer); if (sz < file->size - 4) memset(buffer + sz, ' ', file->size - sz); 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; *(buffer + file->size - 2) = (flags >> 8) & 0xFF; rv = sc_pkcs15init_update_file(profile, p15card, file, buffer, file->size); if (rv > 0) rv = 0; err: sc_file_free(file); free(buffer); LOG_FUNC_RETURN(ctx, rv); } int cosm_delete_file(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_file *df) { struct sc_context *ctx = p15card->card->ctx; struct sc_path path; struct sc_file *parent; int rv = 0; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); 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); 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); LOG_TEST_RET(ctx, rv, "Cannot select parent"); rv = sc_pkcs15init_authenticate(profile, p15card, parent, SC_AC_OP_DELETE); sc_file_free(parent); LOG_TEST_RET(ctx, rv, "Cannot authenticate SC_AC_OP_DELETE"); memset(&path, 0, sizeof(path)); path.type = SC_PATH_TYPE_FILE_ID; path.value[0] = df->id >> 8; path.value[1] = df->id & 0xFF; path.len = 2; rv = sc_delete_file(p15card->card, &path); LOG_FUNC_RETURN(ctx, rv); } /* * Erase the card */ static int cosm_erase_card(struct sc_profile *profile, struct sc_pkcs15_card *p15card) { struct sc_context *ctx = p15card->card->ctx; struct sc_file *df = profile->df_info->file, *dir; int rv; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); /* Delete EF(DIR). This may not be very nice * against other applications that use this file, but * extremely useful for testing :) * Note we need to delete if before the DF because we create * it *after* the DF. * */ if (sc_profile_get_file(profile, "DIR", &dir) >= 0) { 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_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_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; } if (sc_profile_get_file(profile, "public-DF", &dir) >= 0) { 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; } rv = sc_profile_get_file(profile, COSM_TITLE"-AppDF", &dir); if (!rv) { sc_log(ctx, "delete %s; r %i", COSM_TITLE"-AppDF", rv); rv = cosm_delete_file(p15card, profile, dir); sc_file_free(dir); } sc_free_apps(p15card->card); done: if (rv == SC_ERROR_FILE_NOT_FOUND) rv = 0; LOG_FUNC_RETURN(ctx, rv); } static int cosm_create_dir(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_file *df) { struct sc_context *ctx = p15card->card->ctx; struct sc_file *file = NULL; size_t ii; int rv; static const char *create_dfs[] = { COSM_TITLE"-AppDF", "private-DF", "public-DF", COSM_TITLE"-token-info", COSM_TITLE"-puk-file", COSM_TITLE"-container-list", COSM_TITLE"-public-list", COSM_TITLE"-private-list", NULL }; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); /* Oberthur AWP file system is expected.*/ /* Create private objects DF */ for (ii = 0; create_dfs[ii]; ii++) { if (sc_profile_get_file(profile, create_dfs[ii], &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) 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); LOG_FUNC_RETURN(ctx, rv); } static int cosm_create_reference_data(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_pkcs15_auth_info *ainfo, const unsigned char *pin, size_t pin_len, const unsigned char *puk, size_t puk_len ) { struct sc_context *ctx = p15card->card->ctx; struct sc_card *card = p15card->card; 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] = { 0x6F, 0x47, 0xD9, 0x88, 0x4B, 0x6F, 0x9D, 0xC5, 0x78, 0x33, 0x79, 0x8F, 0x5B, 0x7D, 0xE1, 0xA5 }; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); 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) return SC_ERROR_INVALID_ARGUMENTS; if (puk && !puk_len) return SC_ERROR_INVALID_ARGUMENTS; if (ainfo->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return SC_ERROR_OBJECT_NOT_VALID; rv = sc_select_file(card, &ainfo->path, NULL); 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); memset(&args, 0, sizeof(args)); args.type = SC_AC_CHV; args.ref = ainfo->attrs.pin.reference; args.pin = pin; args.pin_len = pin_len; if (!(ainfo->attrs.pin.flags & SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN)) { args.pin_tries = profile_auth_pin.tries_left; if (profile_auth_puk.tries_left > 0) { args.puk = oberthur_puk; args.puk_len = sizeof(oberthur_puk); args.puk_tries = 5; } } else { args.pin_tries = profile_auth_puk.tries_left; } rv = sc_card_ctl(card, SC_CARDCTL_OBERTHUR_CREATE_PIN, &args); 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)) LOG_TEST_RET(ctx, SC_ERROR_INCONSISTENT_PROFILE, "Cannot find PUKFILE"); rv = sc_pkcs15init_update_file(profile, p15card, file, oberthur_puk, sizeof(oberthur_puk)); LOG_TEST_RET(ctx, rv, "Failed to update pukfile"); sc_file_free(file); } LOG_FUNC_RETURN(ctx, rv); } /* * Update PIN */ static int cosm_update_pin(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_pkcs15_auth_info *ainfo, const unsigned char *pin, size_t pin_len, const unsigned char *puk, size_t puk_len ) { struct sc_context *ctx = p15card->card->ctx; int rv; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); if (ainfo->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return SC_ERROR_OBJECT_NOT_VALID; 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) 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); 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); LOG_TEST_RET(ctx, rv, "cosm_update_pin() failed to update tokeninfo"); } LOG_FUNC_RETURN(ctx, rv); } static int cosm_select_pin_reference(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_pkcs15_auth_info *auth_info) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_pin_attributes *pin_attrs; struct sc_file *pinfile; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return SC_ERROR_OBJECT_NOT_VALID; pin_attrs = &auth_info->attrs.pin; 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_log(ctx, "Profile doesn't define \"%s\"", COSM_TITLE "-AppDF"); return SC_ERROR_INCONSISTENT_PROFILE; } if (pin_attrs->flags & SC_PKCS15_PIN_FLAG_LOCAL) auth_info->path = pinfile->path; sc_file_free(pinfile); if (pin_attrs->reference <= 0) { if (pin_attrs->flags & SC_PKCS15_PIN_FLAG_SO_PIN) pin_attrs->reference = 4; else if (pin_attrs->flags & SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN) pin_attrs->reference = 4; else pin_attrs->reference = 1; if (pin_attrs->flags & SC_PKCS15_PIN_FLAG_LOCAL) pin_attrs->reference |= 0x80; } LOG_FUNC_RETURN(ctx, SC_SUCCESS); } /* * Store a PIN */ static int cosm_create_pin(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_file *df, struct sc_pkcs15_object *pin_obj, const unsigned char *pin, size_t pin_len, const unsigned char *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; struct sc_file *pin_file; int rv = 0; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return SC_ERROR_OBJECT_NOT_VALID; pin_attrs = &auth_info->attrs.pin; 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) 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; sc_file_free(pin_file); if (pin_attrs->flags & SC_PKCS15_PIN_FLAG_SO_PIN) { if (pin_attrs->flags & SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN) { LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "SOPIN unblocking is not supported"); } else { if (pin_attrs->reference != 4) 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) LOG_TEST_RET(ctx, SC_ERROR_INVALID_PIN_REFERENCE, "Invalid User PUK reference"); } else { if (pin_attrs->reference != 0x81) 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); LOG_TEST_RET(ctx, rv, "Update PIN failed"); } LOG_FUNC_RETURN(ctx, rv); } /* * Allocate a file */ 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; const char *_template = NULL, *desc = NULL; unsigned int structure = 0xFFFFFFFF; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_log(card->ctx, "cosm_new_file() type %X; num %i",type, num); while (1) { switch (type) { case SC_PKCS15_TYPE_PRKEY_RSA: case COSM_TYPE_PRKEY_RSA: desc = "RSA private key"; _template = "template-private-key"; structure = SC_CARDCTL_OBERTHUR_KEY_RSA_CRT; break; case SC_PKCS15_TYPE_PUBKEY_RSA: case COSM_TYPE_PUBKEY_RSA: desc = "RSA public key"; _template = "template-public-key"; structure = SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC; break; case SC_PKCS15_TYPE_CERT: desc = "certificate"; _template = "template-certificate"; break; case SC_PKCS15_TYPE_DATA_OBJECT: desc = "data object"; _template = "template-public-data"; break; } if (_template) 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_log(card->ctx, "File type %X not supported by card driver", type); return SC_ERROR_INVALID_ARGUMENTS; } type &= SC_PKCS15_TYPE_CLASS_MASK; } sc_log(card->ctx, "cosm_new_file() template %s; num %i",_template, num); if (sc_profile_get_file(profile, _template, &file) < 0) { sc_log(card->ctx, "Profile doesn't define %s template '%s'", desc, _template); LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } file->id |= (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_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; LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } static int cosm_get_temporary_public_key_file(struct sc_card *card, struct sc_file *prvkey_file, struct sc_file **pubkey_file) { struct sc_context *ctx = card->ctx; const struct sc_acl_entry *entry = NULL; struct sc_file *file = NULL; int rv; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (!pubkey_file || !prvkey_file) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); file = sc_file_new(); if (!file) LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); file->status = SC_FILE_STATUS_ACTIVATED; file->type = SC_FILE_TYPE_INTERNAL_EF; file->ef_structure = SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC; file->id = 0x1012; memcpy(&file->path, &prvkey_file->path, sizeof(file->path)); file->path.value[file->path.len - 2] = 0x10; file->path.value[file->path.len - 1] = 0x12; 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); if (!rv) rv = sc_file_add_acl_entry(file, SC_AC_OP_PSO_VERIFY_SIGNATURE, SC_AC_NONE, 0); if (!rv) rv = sc_file_add_acl_entry(file, SC_AC_OP_EXTERNAL_AUTHENTICATE, SC_AC_NONE, 0); if (rv < 0) sc_file_free(file); LOG_TEST_RET(ctx, rv, "Failed to add ACL entry to the temporary public key file"); *pubkey_file = file; LOG_FUNC_RETURN(card->ctx, rv); } static int cosm_generate_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *object, struct sc_pkcs15_pubkey *pubkey) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_prkey_info *key_info = (struct sc_pkcs15_prkey_info *)object->data; struct sc_cardctl_oberthur_genkey_info args; struct sc_file *prkf = NULL, *tmpf = NULL; struct sc_path path; int rv = 0; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); if (object->type != SC_PKCS15_TYPE_PRKEY_RSA) LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Generate key failed: RSA only supported"); path = key_info->path; path.len -= 2; rv = sc_select_file(p15card->card, &path, &tmpf); 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); 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_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); 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); 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); 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; args.id_pub = tmpf->id; args.exponent = 0x10001; 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_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); 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_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_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); key_info->key_reference = prkf->path.value[prkf->path.len - 1] & 0xFF; key_info->path = prkf->path; 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); LOG_FUNC_RETURN(ctx, rv); } /* * Create private key file */ static int cosm_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 = NULL; int rv = 0; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); if (object->type != SC_PKCS15_TYPE_PRKEY_RSA) 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 to bytes long"); 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. * 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); 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_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_VERBOSE, rv, "Failed to delete private key file"); } else if (rv != SC_ERROR_FILE_NOT_FOUND) { 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_VERBOSE, rv, "Failed to create private key file"); key_info->key_reference = file->path.value[file->path.len - 1]; err: sc_file_free(file); LOG_FUNC_RETURN(ctx, rv); } /* * Store a private key */ static int cosm_store_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *object, struct sc_pkcs15_prkey *prkey) { 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; struct sc_cardctl_oberthur_updatekey_info update_info; int rv = 0; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); if (object->type != SC_PKCS15_TYPE_PRKEY_RSA || prkey->algorithm != SC_ALGORITHM_RSA) LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Store key failed: RSA only supported"); 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); LOG_TEST_RET(ctx, rv, "Cannot store key: select key file failed"); rv = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_UPDATE); LOG_TEST_RET(ctx, rv, "No authorisation to store private key"); if (key_info->id.len > sizeof(update_info.id)) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); memset(&update_info, 0, sizeof(update_info)); update_info.type = SC_CARDCTL_OBERTHUR_KEY_RSA_CRT; update_info.data = (void *)&prkey->u.rsa; update_info.data_len = sizeof(void *); update_info.id_len = key_info->id.len; 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); LOG_TEST_RET(ctx, rv, "Cannot update private key"); sc_file_free(file); LOG_FUNC_RETURN(ctx, rv); } #ifdef ENABLE_OPENSSL static int cosm_emu_update_dir (struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_app_info *info) { SC_FUNC_CALLED(p15card->card->ctx, 1); /* No DIR file in the native Oberthur card */ SC_FUNC_RETURN(p15card->card->ctx, 1, SC_SUCCESS); } static int cosm_emu_update_any_df(struct sc_profile *profile, struct sc_pkcs15_card *p15card, unsigned op, struct sc_pkcs15_object *object) { struct sc_context *ctx = p15card->card->ctx; int rv = SC_ERROR_NOT_SUPPORTED; SC_FUNC_CALLED(ctx, 1); switch(op) { case SC_AC_OP_ERASE: 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_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; } SC_FUNC_RETURN(ctx, 1, rv); } static int cosm_emu_update_tokeninfo(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_pkcs15_tokeninfo *tinfo) { struct sc_context *ctx = p15card->card->ctx; struct sc_file *file = NULL; 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)) LOG_TEST_RET(ctx, SC_ERROR_INCONSISTENT_PROFILE, "cannot find "COSM_TITLE"-token-info"); buf = calloc(1, file->size); if (!buf) { sc_file_free(file); SC_FUNC_RETURN(ctx, 1, SC_ERROR_OUT_OF_MEMORY); } label_len = strlen(tinfo->label) > (file->size - 4) ? (file->size - 4) : strlen(tinfo->label); memcpy(buf, tinfo->label, label_len); memset(buf + label_len, ' ', file->size - 4 - label_len); /* current PKCS#11 flags should be read from the token, * but for simplicity assume that user-pin is already initialised -- Andre 2010-10-05 */ flags = COSM_TOKEN_FLAG_TOKEN_INITIALIZED | COSM_TOKEN_FLAG_USER_PIN_INITIALIZED | COSM_TOKEN_FLAG_LOGIN_REQUIRED | COSM_TOKEN_FLAG_PRN_GENERATION; memset(buf + file->size - 4, 0, 4); *(buf + file->size - 1) = flags % 0x100; *(buf + file->size - 2) = (flags % 0x10000) / 0x100; 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); if (rv > 0) rv = 0; SC_FUNC_RETURN(ctx, 1, rv); } static int cosm_emu_write_info(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *pin_obj) { SC_FUNC_CALLED(p15card->card->ctx, 1); /* No OpenSC Info file in the native Oberthur card */ SC_FUNC_RETURN(p15card->card->ctx, 1, SC_SUCCESS); } #endif static struct sc_pkcs15init_operations sc_pkcs15init_oberthur_operations = { cosm_erase_card, NULL, /* init_card */ cosm_create_dir, /* create_dir */ NULL, /* create_domain */ cosm_select_pin_reference, cosm_create_pin, NULL, /* select_key_reference */ cosm_create_key, /* create_key */ cosm_store_key, /* store_key */ cosm_generate_key, /* generate_key */ NULL, NULL, /* encode private/public key */ NULL, /* finalize_card */ NULL, /* delete_object */ #ifdef ENABLE_OPENSSL cosm_emu_update_dir, cosm_emu_update_any_df, cosm_emu_update_tokeninfo, cosm_emu_write_info, NULL, NULL #else NULL, NULL, NULL, NULL, NULL, NULL #endif }; struct sc_pkcs15init_operations * sc_pkcs15init_get_oberthur_ops(void) { return &sc_pkcs15init_oberthur_operations; } OpenSC-0.26.1/src/pkcs15init/pkcs15-oberthur.h000066400000000000000000000047661474147347300206520ustar00rootroot00000000000000#ifndef pkcs15_oberthur_h #define pkcs15_oberthur_h #include #include #include #include #include "config.h" #ifdef ENABLE_OPENSSL #include #include #include #include #include #include #include #include #include #include "profile.h" #include "libopensc/opensc.h" #define COSM_TLV_TAG 0x00 #define TLV_TYPE_V 0 #define TLV_TYPE_LV 1 #define TLV_TYPE_LLV 2 /* Should be greater then SC_PKCS15_TYPE_CLASS_MASK */ #define SC_DEVICE_SPECIFIC_TYPE 0x1000 #define COSM_PUBLIC_LIST (SC_DEVICE_SPECIFIC_TYPE | 0x02) #define COSM_PRIVATE_LIST (SC_DEVICE_SPECIFIC_TYPE | 0x03) #define COSM_CONTAINER_LIST (SC_DEVICE_SPECIFIC_TYPE | 0x04) #define COSM_TOKENINFO (SC_DEVICE_SPECIFIC_TYPE | 0x05) #define COSM_TYPE_PRKEY_RSA (SC_DEVICE_SPECIFIC_TYPE | SC_PKCS15_TYPE_PRKEY_RSA) #define COSM_TYPE_PUBKEY_RSA (SC_DEVICE_SPECIFIC_TYPE | SC_PKCS15_TYPE_PUBKEY_RSA) #define COSM_TYPE_PRIVDATA_OBJECT (SC_DEVICE_SPECIFIC_TYPE | 0x06) #define COSM_TITLE "OberthurAWP" #define COSM_LIST_TAG 0xFF #define COSM_TAG_CONTAINER 0x0000 #define COSM_TAG_CERT 0x0001 #define COSM_TAG_PRVKEY_RSA 0x04B1 #define COSM_TAG_PUBKEY_RSA 0x0349 #define COSM_TAG_DES 0x0679 #define COSM_TAG_DATA 0x0001 #define COSM_IMPORTED 0x0000 #define COSM_GENERATED 0x0004 #define NAME_MAX_LEN 64 #define PUBKEY_512_ASN1_SIZE 0x4A #define PUBKEY_1024_ASN1_SIZE 0x8C #define PUBKEY_2048_ASN1_SIZE 0x10E #define AWP_CONTAINER_RECORD_LEN 12 struct awp_crypto_container { int type; unsigned cert_id; unsigned prkey_id; unsigned pubkey_id; }; struct awp_lv { size_t len; unsigned char *value; }; struct awp_key_info { unsigned flags; unsigned usage; struct awp_lv label; struct awp_lv id; struct awp_lv subject; struct awp_lv exponent, modulus; }; struct awp_cert_info { unsigned flags; struct awp_lv label; struct awp_lv cn, subject, issuer; struct awp_lv id; struct awp_lv serial; X509 *x509; }; struct awp_data_info { unsigned flags; struct awp_lv label, app, oid; }; extern int cosm_delete_file(struct sc_pkcs15_card *, struct sc_profile *, struct sc_file *); extern int awp_update_df_create(struct sc_pkcs15_card *, struct sc_profile *, struct sc_pkcs15_object *); extern int awp_update_df_delete(struct sc_pkcs15_card *, struct sc_profile *, struct sc_pkcs15_object *); #endif /* #ifdef ENABLE_OPENSSL */ #endif /* #ifndef pkcs15_oberthur_h*/ OpenSC-0.26.1/src/pkcs15init/pkcs15-openpgp.c000066400000000000000000000461621474147347300204570ustar00rootroot00000000000000/* * OpenPGP specific operation for PKCS15 initialization * * Copyright (c) 2012 Nguyen Hong Quan . * * 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 "libopensc/opensc.h" #include "libopensc/cardctl.h" #include "libopensc/internal.h" #include "libopensc/log.h" #include "libopensc/cards.h" #include "libopensc/asn1.h" #include "pkcs15-init.h" #include "profile.h" /** * Erase card: erase all EFs/DFs created by OpenSC * @param profile The sc_profile_t object with the configurable profile * information * @param p15card The card from which the opensc application should be * erased. * @return SC_SUCCESS on success and an error code otherwise **/ static int openpgp_erase(struct sc_profile *profile, sc_pkcs15_card_t *p15card) { return SC_ERROR_NOT_SUPPORTED; } /** * Create application DF * @param profile sc_profile_t object with the configurable profile * information * @param p15card sc_card_t object to be used * @param df sc_file_t with the application DF to create * @return SC_SUCCESS on success and an error value otherwise **/ static int openpgp_create_dir(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *df) { return SC_ERROR_NOT_SUPPORTED; } /** * Select PIN reference: do nothing special, the real PIN reference if * determined when the PIN is created. This is just helper function to * determine the next best file id of the PIN file. **/ static int openpgp_select_pin_reference(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_auth_info_t *auth_info) { return SC_ERROR_NOT_SUPPORTED; } /** * Create PIN and, if specified, PUK files * @param profile profile information for this card * @param card sc_card_t object to use * @param pin_obj sc_pkcs15_object_t for the PIN * @param pin PIN value * @param len_len PIN length * @param puk PUK value (optional) * @param puk_len PUK length (optional) * @return SC_SUCCESS on success and an error code otherwise **/ static int openpgp_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) { return SC_ERROR_NOT_SUPPORTED; } /** * Creates empty key file **/ static int openpgp_create_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *obj) { /* For OpenPGP card, the number of keys is fixed, * so this function does not really do anything. * It just present here to avoid pkcs15init's default routine, * which tries to do impossible things. */ LOG_FUNC_CALLED(p15card->card->ctx); LOG_FUNC_RETURN(p15card->card->ctx, SC_SUCCESS); } /** * 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 * @param key the private key * @return SC_SUCCESS on success and an error code otherwise **/ static int openpgp_store_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *obj, sc_pkcs15_prkey_t *key) { sc_card_t *card = p15card->card; sc_pkcs15_prkey_info_t *kinfo = (sc_pkcs15_prkey_info_t *) obj->data; sc_cardctl_openpgp_keystore_info_t key_info; int r; unsigned int i; LOG_FUNC_CALLED(card->ctx); switch(obj->type) { case SC_PKCS15_TYPE_PRKEY_RSA: memset(&key_info, 0, sizeof(sc_cardctl_openpgp_keystore_info_t)); key_info.algorithm = SC_OPENPGP_KEYALGO_RSA; key_info.key_id = kinfo->id.value[0]; key_info.u.rsa.e = key->u.rsa.exponent.data; key_info.u.rsa.e_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.n = key->u.rsa.modulus.data; key_info.u.rsa.n_len = key->u.rsa.modulus.len * 8; /* use bits instead of bytes */ r = sc_card_ctl(card, SC_CARDCTL_OPENPGP_STORE_KEY, &key_info); 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"); LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } memset(&key_info, 0, sizeof(sc_cardctl_openpgp_keystore_info_t)); key_info.algorithm = (kinfo->id.value[0] == 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.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_info.u.ec.ecpointQ = key->u.ec.ecpointQ.value; key_info.u.ec.ecpointQ_len = key->u.ec.ecpointQ.len; /* extract oid the way we need to import it to OpenPGP Card */ if (key->u.ec.params.der.len > 2) key_info.u.ec.oid_len = key->u.ec.params.der.value[1]; else LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); for (i=0; (i < key_info.u.ec.oid_len) && (i+2 < key->u.ec.params.der.len); i++){ key_info.u.ec.oid.value[i] = key->u.ec.params.der.value[i+2]; } key_info.u.ec.oid.value[key_info.u.ec.oid_len] = -1; r = sc_card_ctl(card, SC_CARDCTL_OPENPGP_STORE_KEY, &key_info); break; case SC_PKCS15_TYPE_PRKEY_EDDSA: if (card->type != SC_CARD_TYPE_OPENPGP_GNUK) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "EdDSA keys not supported on this card"); LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } memset(&key_info, 0, sizeof(sc_cardctl_openpgp_keystore_info_t)); key_info.algorithm = (kinfo->id.value[0] == SC_OPENPGP_KEY_ENCR) ? SC_OPENPGP_KEYALGO_ECDH /* ECDH for slot 2 only */ : SC_OPENPGP_KEYALGO_EDDSA; /* EdDSA for slot 1 and 3 */ key_info.key_id = kinfo->id.value[0]; /* TODO Test -- might not work */ key_info.u.ec.privateD = key->u.ec.privateD.data; key_info.u.ec.privateD_len = key->u.ec.privateD.len; key_info.u.ec.ecpointQ = key->u.ec.ecpointQ.value; key_info.u.ec.ecpointQ_len = key->u.ec.ecpointQ.len; r = sc_card_ctl(card, SC_CARDCTL_OPENPGP_STORE_KEY, &key_info); break; default: r = SC_ERROR_NOT_SUPPORTED; sc_log(card->ctx, "%s: Key generation failed: Unknown/unsupported key type.", strerror(r)); } LOG_FUNC_RETURN(card->ctx, r); } /** * 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_rsa(sc_card_t *card, sc_pkcs15_object_t *obj, sc_pkcs15_pubkey_t *pubkey) { sc_context_t *ctx = card->ctx; sc_cardctl_openpgp_keygen_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; 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]; 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.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.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); 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 = 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 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 = 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 err; memcpy(pubkey->u.rsa.exponent.data, key_info.u.rsa.exponent, pubkey->u.rsa.exponent.len); err: free(key_info.u.rsa.modulus); free(key_info.u.rsa.exponent); 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_keygen_info_t key_info; sc_pkcs15_prkey_info_t *required = (sc_pkcs15_prkey_info_t *)obj->data; sc_pkcs15_id_t *kid = &(required->id); const struct sc_ec_parameters *info_ec = (struct sc_ec_parameters *) required->params.data; unsigned int i; 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]; /* set algorithm id based on key reference */ 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 */ /* extract oid the way we need to import it to OpenPGP Card */ if (info_ec->der.len > 2) key_info.u.ec.oid_len = info_ec->der.value[1]; else LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); for (i=0; (i < key_info.u.ec.oid_len) && (i+2 < info_ec->der.len); i++){ key_info.u.ec.oid.value[i] = info_ec->der.value[i+2]; } key_info.u.ec.oid.value[key_info.u.ec.oid_len] = -1; /* Prepare buffer */ key_info.u.ec.ecpoint_len = required->field_length; key_info.u.ec.ecpoint = malloc(key_info.u.ec.ecpoint_len); if (key_info.u.ec.ecpoint == NULL) LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_ENOUGH_MEMORY); /* 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 = SC_ALGORITHM_EC; pubkey->u.ec.ecpointQ.len = key_info.u.ec.ecpoint_len; pubkey->u.ec.ecpointQ.value = malloc(key_info.u.ec.ecpoint_len); if (pubkey->u.ec.ecpointQ.value == NULL) goto err; memcpy(pubkey->u.ec.ecpointQ.value, key_info.u.ec.ecpoint, key_info.u.ec.ecpoint_len); err: if (key_info.u.ec.ecpoint) free(key_info.u.ec.ecpoint); 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: if (card->type != SC_CARD_TYPE_OPENPGP_GNUK) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "EdDSA is 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); } static int openpgp_emu_update_any_df(sc_profile_t *profile, sc_pkcs15_card_t *p15card, unsigned operation, sc_pkcs15_object_t *obj) { LOG_FUNC_CALLED(p15card->card->ctx); /* After storing object, pkcs15init will call this function to update DF. * But OpenPGP has no other DF than OpenPGP-Application, so we do nothing. */ LOG_FUNC_RETURN(p15card->card->ctx, SC_SUCCESS); } static int openpgp_emu_update_tokeninfo(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_tokeninfo_t *tokeninfo) { LOG_FUNC_CALLED(p15card->card->ctx); /* When unbinding pkcs15init, this function will be called. * But for OpenPGP, token info does not need to change, we do nothing. */ LOG_FUNC_RETURN(p15card->card->ctx, SC_SUCCESS); } static int openpgp_store_data(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15_object *obj, struct sc_pkcs15_der *content, struct sc_path *path) { sc_card_t *card = p15card->card; sc_context_t *ctx = card->ctx; sc_file_t *file = NULL; sc_pkcs15_cert_info_t *cinfo; sc_pkcs15_id_t *cid; sc_pkcs15_data_info_t *dinfo; u8 buf[254]; int r; LOG_FUNC_CALLED(card->ctx); switch (obj->type & SC_PKCS15_TYPE_CLASS_MASK) { case SC_PKCS15_TYPE_PRKEY: case SC_PKCS15_TYPE_PUBKEY: /* For these two type, store_data just don't need to do anything. * All have been done already before this function is called */ r = SC_SUCCESS; break; 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)); LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); } /* OpenPGP card v.2 contains only 1 certificate */ 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); } /* 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, tag, (const unsigned char *) content->value, content->len); break; case SC_PKCS15_TYPE_DATA_OBJECT: dinfo = (sc_pkcs15_data_info_t *) obj->data; /* dinfo->app_label contains filename */ sc_log(ctx, "===== App label %s", dinfo->app_label); /* Currently, we only support DO 0101. The reason is that when initializing this * pkcs15 emulation, PIN authentication is not applied and we can expose only this DO, * which is "read always". * If we support other DOs, they will not be exposed, and not helpful to user. * I haven't found a way to refresh the list of exposed DOs after verifying PIN yet. * http://sourceforge.net/mailarchive/message.php?msg_id=30646373 **/ sc_log(ctx, "About to write to DO 0101"); sc_format_path("0101", path); r = sc_select_file(card, path, &file); LOG_TEST_RET(card->ctx, r, "Cannot select private DO"); r = sc_read_binary(card, 0, buf, sizeof(buf), 0); if (r < 0) { sc_log(ctx, "Cannot read DO 0101"); break; } if (r > 0) { sc_log(ctx, "DO 0101 is full."); r = SC_ERROR_NOT_ENOUGH_MEMORY; break; } r = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_UPDATE); if (r >= 0 && content->len) { r = sc_update_binary(p15card->card, 0, (const unsigned char *) content->value, content->len, 0); } break; default: 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 */ }; struct sc_pkcs15init_operations *sc_pkcs15init_get_openpgp_ops(void) { return &sc_pkcs15init_openpgp_operations; } OpenSC-0.26.1/src/pkcs15init/pkcs15-rtecp.c000066400000000000000000000525131474147347300201210ustar00rootroot00000000000000/* * pkcs15-rtecp.c: Rutoken ECP specific operation for PKCS15 initialization * * Copyright (C) 2009 Aleksey Samsonov * * 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 "libopensc/opensc.h" #include "libopensc/cardctl.h" #include "libopensc/log.h" #include "libopensc/pkcs15.h" #include "pkcs15-init.h" #include "profile.h" #define RTECP_SO_PIN_REF 1 #define RTECP_USER_PIN_REF 2 /* * Erase everything that's on the card */ static int rtecp_erase(sc_profile_t *profile, sc_pkcs15_card_t *p15card) { int r; if (!profile || !p15card || !p15card->card) return SC_ERROR_INVALID_ARGUMENTS; r = sc_card_ctl(p15card->card, SC_CARDCTL_RTECP_INIT, NULL); if (r == SC_SUCCESS) sc_free_apps(p15card->card); return r; } static int create_sysdf(sc_profile_t *profile, sc_card_t *card, const char *name) { sc_file_t *file; sc_path_t path; int r; assert(profile && card && card->ctx && name); r = sc_profile_get_file(profile, name, &file); if (r == SC_SUCCESS) { assert(file); path = file->path; assert(path.len > 2); if (path.len > 2) path.len -= 2; r = sc_select_file(card, &path, NULL); if (r == SC_SUCCESS) r = sc_file_add_acl_entry(file, SC_AC_OP_CREATE, SC_AC_CHV, RTECP_USER_PIN_REF); if (r == SC_SUCCESS) r = sc_file_add_acl_entry(file, SC_AC_OP_DELETE, SC_AC_NEVER, SC_AC_KEY_REF_NONE); if (r == SC_SUCCESS) r = sc_create_file(card, file); sc_file_free(file); } sc_log(card->ctx, "Create %s failed: %s\n", name, sc_strerror(r)); return r; } /* * Card-specific initialization of PKCS15 meta-information */ static int rtecp_init(sc_profile_t *profile, sc_pkcs15_card_t *p15card) { sc_card_t *card; sc_file_t *file; int r; if (!profile || !p15card || !p15card->card || !p15card->card->ctx) return SC_ERROR_INVALID_ARGUMENTS; card = p15card->card; r = sc_profile_get_file(profile, "MF", &file); LOG_TEST_RET(card->ctx, r, "Get MF info failed"); assert(file); r = sc_create_file(card, file); sc_file_free(file); LOG_TEST_RET(card->ctx, r, "Create MF failed"); r = sc_profile_get_file(profile, "DIR", &file); LOG_TEST_RET(card->ctx, r, "Get DIR file info failed"); assert(file); r = sc_create_file(card, file); sc_file_free(file); LOG_TEST_RET(card->ctx, r, "Create DIR file failed"); create_sysdf(profile, card, "Sys-DF"); create_sysdf(profile, card, "SysKey-DF"); create_sysdf(profile, card, "PuKey-DF"); create_sysdf(profile, card, "PrKey-DF"); create_sysdf(profile, card, "SKey-DF"); create_sysdf(profile, card, "Cer-DF"); create_sysdf(profile, card, "LCHV-DF"); create_sysdf(profile, card, "Resrv1-DF"); 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); } /* * Create a DF */ static int rtecp_create_dir(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *df) { if (!profile || !p15card || !p15card->card || !df) return SC_ERROR_INVALID_ARGUMENTS; return sc_create_file(p15card->card, df); } /* * Select a PIN reference */ static int rtecp_select_pin_reference(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_auth_info_t *auth_info) { int pin_ref; if (!profile || !p15card || !p15card->card || !p15card->card->ctx || !auth_info) return SC_ERROR_INVALID_ARGUMENTS; 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) pin_ref = RTECP_SO_PIN_REF; else pin_ref = RTECP_USER_PIN_REF; if (auth_info->attrs.pin.reference != pin_ref) LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_NOT_SUPPORTED); return SC_SUCCESS; } /* * Create a PIN object within the given DF */ static int rtecp_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_context_t *ctx; sc_pkcs15_auth_info_t *auth_info; sc_file_t *file = NULL; /* GCHV min-length Flags Attempts Reserve */ unsigned char prop[] = { 0x01, '?', 0x01, '?', 0, 0 }; /* AccessMode Unblock Change Delete */ unsigned char sec[15] = { 0x43, '?', '?', 0, 0, 0, 0, 0xFF }; char pin_sname[0x10]; int r, reset_by_sopin = 0; (void)puk; /* no warning */ if (!profile || !p15card || !p15card->card || !p15card->card->ctx || !df || !pin_obj || !pin_obj->data || !pin || !pin_len) return SC_ERROR_INVALID_ARGUMENTS; ctx = p15card->card->ctx; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); if (puk_len != 0) { sc_log(ctx, "Do not enter User unblocking PIN (PUK): %s\n", sc_strerror(SC_ERROR_NOT_SUPPORTED)); return SC_ERROR_NOT_SUPPORTED; } auth_info = (sc_pkcs15_auth_info_t *)pin_obj->data; if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return SC_ERROR_OBJECT_NOT_VALID; if (auth_info->attrs.pin.reference != RTECP_SO_PIN_REF && auth_info->attrs.pin.reference != RTECP_USER_PIN_REF) { sc_log(ctx, "PIN reference %i not found in standard" " (Rutoken ECP) PINs\n", auth_info->attrs.pin.reference); return SC_ERROR_NOT_SUPPORTED; } snprintf(pin_sname, sizeof(pin_sname), "CHV%i", auth_info->attrs.pin.reference); if (auth_info->attrs.pin.reference == RTECP_USER_PIN_REF) { r = sc_profile_get_file(profile, pin_sname, &file); if (!r) { const struct sc_acl_entry *acl = NULL; r = sc_pkcs15init_fixup_file(profile, p15card, file); if (r < 0) sc_file_free(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_log(ctx, "Allow reset of User PIN with SoPIN\n"); reset_by_sopin = 1; } sc_file_free(file); } } file = sc_file_new(); if (!file) 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); 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); 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)); } if (r == SC_SUCCESS) r = sc_file_set_type_attr(file, (const u8*)"\x10\x00", 2); if (r == SC_SUCCESS) r = sc_create_file(p15card->card, file); sc_file_free(file); 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); LOG_FUNC_RETURN(ctx, r); } /* * Select a reference for a private key object */ static int rtecp_select_key_reference(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_prkey_info_t *key_info) { sc_file_t *df; int r; if (!profile || !p15card || !p15card->card || !p15card->card->ctx || !key_info) return SC_ERROR_INVALID_ARGUMENTS; if (key_info->key_reference <= 0) key_info->key_reference = 1; else if (key_info->key_reference > 0xFF) return SC_ERROR_TOO_MANY_OBJECTS; r = sc_profile_get_file(profile, "PrKey-DF", &df); LOG_TEST_RET(p15card->card->ctx, r, "Get PrKey-DF info failed"); assert(df); key_info->path = df->path; sc_file_free(df); r = sc_append_file_id(&key_info->path, key_info->key_reference); return r; } /* * Create an empty key object */ static int rtecp_create_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *obj) { sc_context_t *ctx; /* RSA_PRkey/ Adds Miller- * RSA_PUBkey Rabin tests Attempts Reserve */ const unsigned char prkey_prop[] = { 0x23, 0, 0, 0xAA, 0, 0 }; const unsigned char pbkey_prop[] = { 0x33, 0, 0, 0xAA, 0, 0 }; /* GOSTR3410_PRkey/ * GOSTR3410_PUBkey paramset Attempts Reserve */ unsigned char prgkey_prop[] = { 0x03, '?', 0, 0xAA, 0, 0 }; unsigned char pbgkey_prop[] = { 0x13, '?', 0, 0xAA, 0, 0 }; /* AccessMode - Update Use - - - Delete */ unsigned char prkey_sec[15] = { 0x46, 0, '?', '?', 0, 0, 0, '?' }; unsigned char pbkey_sec[15] = { 0x46, 0, '?', 0, 0, 0, 0, '?' }; unsigned char auth_id, paramset; sc_pkcs15_prkey_info_t *key_info; sc_file_t *file; int r; if (!profile || !p15card || !p15card->card || !p15card->card->ctx || !obj || !obj->data) return SC_ERROR_INVALID_ARGUMENTS; ctx = p15card->card->ctx; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA && obj->type != SC_PKCS15_TYPE_PRKEY_GOSTR3410) return SC_ERROR_NOT_SUPPORTED; if (obj->auth_id.len != 1) return SC_ERROR_INVALID_ARGUMENTS; auth_id = obj->auth_id.value[0]; key_info = (sc_pkcs15_prkey_info_t *)obj->data; assert(key_info); 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_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) { if (key_info->params.len < sizeof(int)) return SC_ERROR_INVALID_ARGUMENTS; if (((int*)key_info->params.data)[0] < 1 || ((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); prgkey_prop[1] = 0x10 + (paramset << 4); pbgkey_prop[1] = prgkey_prop[1]; } r = sc_profile_get_file(profile, "PKCS15-AppDF", &file); 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); LOG_TEST_RET(ctx, r, "Authenticate failed"); file = sc_file_new(); if (!file) 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); /* private key file */ if (obj->type == SC_PKCS15_TYPE_PRKEY_RSA) file->size = key_info->modulus_length / 8 / 2 * 5 + 8; else file->size = key_info->modulus_length / 8; if (r == SC_SUCCESS) { assert(sizeof(prkey_sec)/sizeof(prkey_sec[0]) > 7); prkey_sec[2] = auth_id; prkey_sec[3] = auth_id; prkey_sec[7] = auth_id; r = sc_file_set_sec_attr(file, prkey_sec, sizeof(prkey_sec)); } if (r == SC_SUCCESS) { if (obj->type == SC_PKCS15_TYPE_PRKEY_RSA) r = sc_file_set_prop_attr(file, prkey_prop, sizeof(prkey_prop)); else r = sc_file_set_prop_attr(file, prgkey_prop,sizeof(prgkey_prop)); } if (r == SC_SUCCESS) { sc_log(ctx, "create private key file id:%04i", file->id); r = sc_create_file(p15card->card, file); } /* public key file */ if (obj->type == SC_PKCS15_TYPE_PRKEY_RSA) file->size = key_info->modulus_length / 8 / 2 * 3; else file->size = key_info->modulus_length / 8 * 2; if (r == SC_SUCCESS) { assert(sizeof(pbkey_sec)/sizeof(pbkey_sec[0]) > 7); pbkey_sec[2] = auth_id; pbkey_sec[7] = auth_id; r = sc_file_set_sec_attr(file, pbkey_sec, sizeof(pbkey_sec)); } if (r == SC_SUCCESS) { if (obj->type == SC_PKCS15_TYPE_PRKEY_RSA) r = sc_file_set_prop_attr(file, pbkey_prop, sizeof(pbkey_prop)); else r = sc_file_set_prop_attr(file, pbgkey_prop,sizeof(pbgkey_prop)); } if (r == SC_SUCCESS) { sc_log(ctx, "create public key file id:%04i", file->id); r = sc_create_file(p15card->card, file); } sc_file_free(file); LOG_FUNC_RETURN(ctx, r); } /* * Store a key on the card */ static int rtecp_store_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *obj, sc_pkcs15_prkey_t *key) { sc_card_t *card; sc_pkcs15_prkey_info_t *key_info; sc_file_t *pukey_df; sc_path_t path; unsigned char *buf; size_t buf_len, key_len, len, i; int r; if (!profile || !p15card || !p15card->card || !p15card->card->ctx || !obj || !obj->data || !key) return SC_ERROR_INVALID_ARGUMENTS; card = p15card->card; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if ((obj->type != SC_PKCS15_TYPE_PRKEY_RSA || key->algorithm != SC_ALGORITHM_RSA) && (obj->type != SC_PKCS15_TYPE_PRKEY_GOSTR3410 || key->algorithm != SC_ALGORITHM_GOSTR3410)) return SC_ERROR_NOT_SUPPORTED; key_info = (sc_pkcs15_prkey_info_t *)obj->data; assert(key_info); if (key->algorithm == SC_ALGORITHM_RSA) { assert(key_info->modulus_length % 128 == 0); 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); len = key_info->modulus_length / 8; key_len = len; buf_len = len; } if (key->algorithm == SC_ALGORITHM_RSA && (!key->u.rsa.p.data || !key->u.rsa.q.data || !key->u.rsa.iqmp.data || !key->u.rsa.dmp1.data || !key->u.rsa.dmq1.data || !key->u.rsa.modulus.data || !key->u.rsa.exponent.data || key->u.rsa.p.len != len || key->u.rsa.q.len != len || key->u.rsa.iqmp.len != len || key->u.rsa.dmp1.len != len || key->u.rsa.dmq1.len != len || key->u.rsa.modulus.len != 2*len || key->u.rsa.exponent.len > len || key->u.rsa.exponent.len == 0)) return SC_ERROR_INVALID_ARGUMENTS; if (key->algorithm == SC_ALGORITHM_GOSTR3410 && (!key->u.gostr3410.d.data || key->u.gostr3410.d.len != len)) return SC_ERROR_INVALID_ARGUMENTS; buf = calloc(1, buf_len); if (!buf) LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); assert(key_len <= buf_len); if (key->algorithm == SC_ALGORITHM_RSA) { /* p */ for (i = 0; i < len; ++i) buf[i] = key->u.rsa.p.data[len - 1 - i]; /* q */ for (i = 0; i < len; ++i) buf[len + 4 + i] = key->u.rsa.q.data[len - 1 - i]; /* iqmp */ for (i = 0; i < len; ++i) buf[len + 4 + len + 4 + i] = key->u.rsa.iqmp.data[len - 1 - i]; /* dmp1 */ for (i = 0; i < len; ++i) buf[len + 4 + len + 4 + len + i] = key->u.rsa.dmp1.data[len - 1 - i]; /* dmq1 */ for (i = 0; i < len; ++i) buf[len * 4 + 8 + i] = key->u.rsa.dmq1.data[len - 1 - i]; } else { /* d */ for (i = 0; i < len; ++i) buf[i] = key->u.gostr3410.d.data[len - 1 - i]; } path = key_info->path; 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->algorithm == SC_ALGORITHM_RSA) { /* modulus */ for (i = 0; i < 2*len; ++i) buf[i] = key->u.rsa.modulus.data[2*len - 1 - i]; /* exponent */ for (i = 0; i < key->u.rsa.exponent.len && i < len; ++i) buf[2 * len + i] = key->u.rsa.exponent.data[ key->u.rsa.exponent.len - 1 - i]; } if (r == SC_SUCCESS) { r = sc_profile_get_file(profile, "PuKey-DF", &pukey_df); if (r == SC_SUCCESS) { assert(pukey_df); 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_log(card->ctx, "%s\n", "Get PuKey-DF info failed"); } if (r == SC_SUCCESS) { 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); if (r && card->ctx->debug >= 2) sc_log(card->ctx, "%s\n", "Store public key failed"); } end: assert(buf); free(buf); LOG_FUNC_RETURN(card->ctx, r); } /* * Generate key */ static int rtecp_generate_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *obj, sc_pkcs15_pubkey_t *pubkey) { sc_context_t *ctx; sc_pkcs15_prkey_info_t *key_info; sc_rtecp_genkey_data_t data; int r; if (!profile || !p15card || !p15card->card || !p15card->card->ctx || !obj || !obj->data || !pubkey) return SC_ERROR_INVALID_ARGUMENTS; ctx = p15card->card->ctx; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); switch (obj->type) { case SC_PKCS15_TYPE_PRKEY_RSA: data.type = SC_ALGORITHM_RSA; break; case SC_PKCS15_TYPE_PRKEY_GOSTR3410: data.type = SC_ALGORITHM_GOSTR3410; break; default: return SC_ERROR_NOT_SUPPORTED; } key_info = (sc_pkcs15_prkey_info_t *)obj->data; assert(key_info); data.key_id = key_info->key_reference; assert(data.key_id != 0); switch (data.type) { case SC_ALGORITHM_RSA: assert(key_info->modulus_length % 128 == 0); 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; data.u.rsa.exponent = calloc(1, data.u.rsa.exponent_len); if (!data.u.rsa.modulus || !data.u.rsa.exponent) { free(data.u.rsa.modulus); free(data.u.rsa.exponent); LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); } break; case SC_ALGORITHM_GOSTR3410: assert(key_info->modulus_length == SC_PKCS15_GOSTR3410_KEYSIZE); 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); LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); } break; default: assert(0); } 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) { case SC_ALGORITHM_RSA: pubkey->u.rsa.modulus.data = data.u.rsa.modulus; pubkey->u.rsa.modulus.len = data.u.rsa.modulus_len; pubkey->u.rsa.exponent.data = data.u.rsa.exponent; pubkey->u.rsa.exponent.len = data.u.rsa.exponent_len; break; case SC_ALGORITHM_GOSTR3410: pubkey->u.gostr3410.xy.data = data.u.gostr3410.xy; pubkey->u.gostr3410.xy.len = data.u.gostr3410.xy_len; break; } } LOG_FUNC_RETURN(ctx, r); } /* * Finalize card * Ends the initialization phase of the smart card/token */ static int rtecp_finalize(sc_card_t *card) { if (!card) return SC_ERROR_INVALID_ARGUMENTS; 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, struct sc_pkcs15_object *obj, const struct sc_path *path) { sc_context_t *ctx; sc_file_t *df; sc_path_t pubkey_path; int key_ref; int r; if (!profile || !p15card || !p15card->card || !p15card->card->ctx) return SC_ERROR_INVALID_ARGUMENTS; ctx = p15card->card->ctx; LOG_FUNC_CALLED(ctx); sc_log(ctx, "delete object: type %X, path %s", obj->type, sc_print_path(path)); if ((obj->type & SC_PKCS15_TYPE_CLASS_MASK) != SC_PKCS15_TYPE_PRKEY) LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); key_ref = ((struct sc_pkcs15_prkey_info *)obj->data)->key_reference; sc_log(ctx, "key reference %04i", key_ref); r = sc_profile_get_file(profile, "PuKey-DF", &df); LOG_TEST_RET(ctx, r, "Get PuKey-DF info failed"); pubkey_path = df->path; sc_file_free(df); r = sc_append_file_id(&pubkey_path, key_ref); LOG_TEST_RET(ctx, r, "Append ID to file failed"); sc_log(ctx, "delete pubkey file %s", sc_print_path(&pubkey_path)); r = sc_pkcs15init_delete_by_path(profile, p15card, &pubkey_path); if (r && r != SC_ERROR_FILE_NOT_FOUND) LOG_FUNC_RETURN(ctx, r); LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); } static struct sc_pkcs15init_operations sc_pkcs15init_rtecp_operations = { rtecp_erase, /* erase_card */ rtecp_init, /* init_card */ rtecp_create_dir, /* create_dir */ NULL, /* create_domain */ rtecp_select_pin_reference, /* select_pin_reference */ rtecp_create_pin, /* create_pin */ rtecp_select_key_reference, /* select_key_reference */ rtecp_create_key, /* create_key */ rtecp_store_key, /* store_key */ rtecp_generate_key, /* generate_key */ NULL, /* encode_private_key */ NULL, /* encode_public_key */ rtecp_finalize, /* finalize_card */ rtecp_delete_object, /* delete_object */ NULL, NULL, NULL, NULL, NULL, /* pkcs15init emulation */ NULL /* sanity_check */ }; struct sc_pkcs15init_operations * sc_pkcs15init_get_rtecp_ops(void) { return &sc_pkcs15init_rtecp_operations; } OpenSC-0.26.1/src/pkcs15init/pkcs15-rutoken.c000066400000000000000000000223671474147347300204770ustar00rootroot00000000000000/* * Rutoken S specific operation for PKCS15 initialization * * Copyright (C) 2007 Pavel Mironchik * Copyright (C) 2007 Eugene Hermann * * 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 "libopensc/opensc.h" #include "libopensc/cardctl.h" #include "libopensc/log.h" #include "libopensc/pkcs15.h" #include "pkcs15-init.h" #include "profile.h" static const sc_SecAttrV2_t wn_sec_attr = { 0x43, 1, 1, 0, 0, 0, 0, -1, 2, 0, 0, 0, 2 }; static const sc_SecAttrV2_t p2_sec_attr = { 0x43, 1, 1, 0, 0, 0, 0, -1, 1, 0, 0, 0, 2 }; static const sc_SecAttrV2_t p1_sec_attr = { 0x43, -1, 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1 }; static const struct { u8 id, options, flags, try, pass[8]; sc_SecAttrV2_t const* p_sattr; } do_pins[] = { { SC_RUTOKEN_DEF_ID_GCHV_USER, SC_RUTOKEN_OPTIONS_GACCESS_USER, SC_RUTOKEN_FLAGS_COMPACT_DO, 0xFF, { '1', '2', '3', '4', '5', '6', '7', '8' }, &p2_sec_attr }, { SC_RUTOKEN_DEF_ID_GCHV_ADMIN, SC_RUTOKEN_OPTIONS_GACCESS_ADMIN, SC_RUTOKEN_FLAGS_COMPACT_DO, 0xFF, { '8', '7', '6', '5', '4', '3', '2', '1' }, &p1_sec_attr } }; /* * Create a DF */ static int rutoken_create_dir(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *df) { if (!profile || !p15card || !p15card->card || !p15card->card->ctx || !df) return SC_ERROR_INVALID_ARGUMENTS; SC_FUNC_CALLED(p15card->card->ctx, SC_LOG_DEBUG_VERBOSE); return sc_pkcs15init_create_file(profile, p15card, df); } /* * Select a PIN reference */ static int rutoken_select_pin_reference(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_auth_info_t *auth_info) { int pin_ref; unsigned int so_pin_flag; if (!profile || !p15card || !p15card->card || !p15card->card->ctx || !auth_info) return SC_ERROR_INVALID_ARGUMENTS; 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; pin_ref = auth_info->attrs.pin.reference; so_pin_flag = auth_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN; 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) || (pin_ref == SC_RUTOKEN_DEF_ID_GCHV_USER && !so_pin_flag) ) return SC_SUCCESS; else return SC_ERROR_NOT_SUPPORTED; } /* * Create a PIN object within the given DF */ static int rutoken_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_context_t *ctx; sc_pkcs15_auth_info_t *auth_info; size_t i; (void)puk; /* no warning */ if (!profile || !p15card || !p15card->card || !p15card->card->ctx || !df || !pin_obj || !pin_obj->data || !pin || !pin_len) return SC_ERROR_INVALID_ARGUMENTS; ctx = p15card->card->ctx; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); if (puk_len != 0) { sc_log(ctx, "Do not enter User unblocking PIN (PUK): %s\n", sc_strerror(SC_ERROR_NOT_SUPPORTED)); return SC_ERROR_NOT_SUPPORTED; } auth_info = (sc_pkcs15_auth_info_t *)pin_obj->data; if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return SC_ERROR_OBJECT_NOT_VALID; for (i = 0; i < sizeof(do_pins)/sizeof(do_pins[0]); ++i) if (auth_info->attrs.pin.reference == do_pins[i].id) { if (pin_len == sizeof(do_pins[i].pass) && memcmp(do_pins[i].pass, pin, pin_len) == 0 ) return SC_SUCCESS; else { sc_log(ctx, "Incorrect PIN\n"); break; } } sc_log(ctx, "PIN reference %i not found in standard (Rutoken) PINs\n", auth_info->attrs.pin.reference); return SC_ERROR_NOT_SUPPORTED; } /* * Initialization routine */ static int create_pins(sc_card_t *card) { sc_DO_V2_t param_do; size_t i; int r = SC_SUCCESS; for (i = 0; i < sizeof(do_pins)/sizeof(do_pins[0]); ++i) { memset(¶m_do, 0, sizeof(param_do)); param_do.HDR.OTID.byObjectType = SC_RUTOKEN_TYPE_CHV; param_do.HDR.OTID.byObjectID = do_pins[i].id; param_do.HDR.OP.byObjectOptions = do_pins[i].options; 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); if (r != SC_SUCCESS) break; } return r; } static int create_typical_fs(sc_card_t *card) { sc_file_t *df; int r; df = sc_file_new(); if (!df) return SC_ERROR_OUT_OF_MEMORY; df->type = SC_FILE_TYPE_DF; do { r = sc_file_set_sec_attr(df, wn_sec_attr, sizeof(wn_sec_attr)); if (r != SC_SUCCESS) break; /* Create MF 3F00 */ df->id = 0x3F00; sc_format_path("3F00", &df->path); r = sc_create_file(card, df); if (r != SC_SUCCESS) break; /* Create 3F00/0000 */ df->id = 0x0000; sc_append_file_id(&df->path, df->id); r = sc_create_file(card, df); if (r != SC_SUCCESS) break; /* Create 3F00/0000/0000 */ df->id = 0x0000; sc_append_file_id(&df->path, df->id); r = sc_create_file(card, df); if (r != SC_SUCCESS) break; /* Create USER PIN and SO PIN*/ r = create_pins(card); if (r != SC_SUCCESS) break; /* VERIFY USER PIN */ r = sc_verify(card, SC_AC_CHV, do_pins[0].id, do_pins[0].pass, sizeof(do_pins[0].pass), NULL); if (r != SC_SUCCESS) break; /* Create 3F00/0000/0000/0001 */ df->id = 0x0001; sc_append_file_id(&df->path, df->id); r = sc_create_file(card, df); if (r != SC_SUCCESS) break; sc_format_path("3F0000000000", &df->path); r = sc_select_file(card, &df->path, NULL); if (r != SC_SUCCESS) break; /* Create 3F00/0000/0000/0002 */ df->id = 0x0002; sc_append_file_id(&df->path, df->id); r = sc_create_file(card, df); if (r != SC_SUCCESS) break; sc_format_path("3F000000", &df->path); r = sc_select_file(card, &df->path, NULL); if (r != SC_SUCCESS) break; /* Create 3F00/0000/0001 */ df->id = 0x0001; sc_append_file_id(&df->path, df->id); r = sc_create_file(card, df); if (r != SC_SUCCESS) break; /* RESET ACCESS RIGHTS */ r = sc_logout(card); } while(0); sc_file_free(df); return r; } /* * Erase everything that's on the card */ static int rutoken_erase(struct sc_profile *profile, sc_pkcs15_card_t *p15card) { sc_card_t *card; int ret, ret_end; if (!profile || !p15card || !p15card->card || !p15card->card->ctx) return SC_ERROR_INVALID_ARGUMENTS; card = p15card->card; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); /* ret = sc_card_ctl(card, SC_CARDCTL_ERASE_CARD, NULL); */ ret = sc_card_ctl(card, SC_CARDCTL_RUTOKEN_FORMAT_INIT, NULL); if (ret == SC_SUCCESS) { ret = create_typical_fs(card); if (ret != SC_SUCCESS) 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); if (ret_end != SC_SUCCESS) ret = ret_end; } if (ret != SC_SUCCESS) sc_log(card->ctx, "Failed to erase: %s\n", sc_strerror(ret)); else sc_free_apps(card); return ret; } static struct sc_pkcs15init_operations sc_pkcs15init_rutoken_operations = { rutoken_erase, /* erase_card */ NULL, /* init_card */ rutoken_create_dir, /* create_dir */ NULL, /* create_domain */ rutoken_select_pin_reference, /* select_pin_reference */ rutoken_create_pin, /* create_pin */ NULL, /* select_key_reference */ NULL, /* create_key */ NULL, /* store_key */ NULL, /* generate_key */ NULL, /* encode_private_key */ NULL, /* encode_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_rutoken_ops(void) { return &sc_pkcs15init_rutoken_operations; } OpenSC-0.26.1/src/pkcs15init/pkcs15-sc-hsm.c000066400000000000000000000441321474147347300201740ustar00rootroot00000000000000/* * pkcs15-sc-hsm.c : PKCS#15 emulation for write support * * Copyright (C) 2012 Andreas Schwier, CardContact, Minden, Germany * * 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 #include #include #include #include #include #include "../libopensc/opensc.h" #include "../libopensc/cardctl.h" #include "../libopensc/log.h" #include "../libopensc/pkcs15.h" #include "../libopensc/cards.h" #include "../libopensc/card-sc-hsm.h" #include "../libopensc/asn1.h" #include "../libopensc/pkcs15.h" #include "common/compat_strlcpy.h" #include "common/compat_strlcat.h" #include "pkcs15-init.h" #include "profile.h" static u8 pubexp[] = { 0x01, 0x00, 0x01 }; static int sc_hsm_delete_ef(sc_pkcs15_card_t *p15card, u8 prefix, u8 id) { sc_card_t *card = p15card->card; sc_path_t path; u8 fid[2]; int r; fid[0] = prefix; fid[1] = id; sc_path_set(&path, SC_PATH_TYPE_FILE_ID, fid, 2, 0, -1); r = sc_delete_file(card, &path); LOG_TEST_RET(card->ctx, r, "Could not delete file"); LOG_FUNC_RETURN(card->ctx, r); } static int sc_hsm_update_ef(sc_pkcs15_card_t *p15card, u8 prefix, u8 id, int erase, u8 *buf, size_t buflen) { sc_card_t *card = p15card->card; sc_file_t *file = NULL; sc_path_t path; u8 fid[2]; int r; fid[0] = prefix; fid[1] = id; sc_path_set(&path, SC_PATH_TYPE_FILE_ID, fid, 2, 0, -1); r = sc_select_file(card, &path, NULL); if ((r == SC_SUCCESS) && erase) { r = sc_delete_file(card, &path); LOG_TEST_RET(card->ctx, r, "Could not delete file"); r = SC_ERROR_FILE_NOT_FOUND; } if (r == SC_ERROR_FILE_NOT_FOUND) { file = sc_file_new(); file->id = (path.value[0] << 8) | path.value[1]; file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = SC_FILE_EF_TRANSPARENT; file->size = (size_t) 0; file->status = SC_FILE_STATUS_ACTIVATED; r = sc_create_file(card, file); sc_file_free(file); LOG_TEST_RET(card->ctx, r, "Could not create file"); } r = sc_update_binary(card, 0, buf, buflen, 0); LOG_FUNC_RETURN(card->ctx, r); } static int sc_hsm_create_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *obj) { // Keys are automatically generated in GENERATE ASYMMETRIC KEY PAIR command LOG_FUNC_CALLED(p15card->card->ctx); LOG_FUNC_RETURN(p15card->card->ctx, SC_SUCCESS); } static int sc_hsm_store_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *obj, sc_pkcs15_prkey_t *key) { LOG_FUNC_CALLED(p15card->card->ctx); LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_NOT_SUPPORTED); } static int sc_hsm_determine_free_id(struct sc_pkcs15_card *p15card, u8 range) { struct sc_card *card = p15card->card; u8 filelist[MAX_EXT_APDU_LENGTH]; int filelistlength, i, j; LOG_FUNC_CALLED(p15card->card->ctx); filelistlength = sc_list_files(card, filelist, sizeof(filelist)); LOG_TEST_RET(card->ctx, filelistlength, "Could not enumerate file and key identifier"); for (j = 0; j < 256; j++) { for (i = 0; i + 1 < filelistlength; i += 2) { if ((filelist[i] == range) && (filelist[i + 1] == j)) { break; } } if (i >= filelistlength) { LOG_FUNC_RETURN(p15card->card->ctx, j); } } LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_NOT_ENOUGH_MEMORY); } 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); cvc->coefficientAorExponentlen = sizeof(pubexp); cvc->coefficientAorExponent = malloc(sizeof(pubexp)); if (!cvc->coefficientAorExponent) { LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_OUT_OF_MEMORY); } memcpy(cvc->coefficientAorExponent, pubexp, sizeof(pubexp)); cvc->pukoid = rsa15withSHA256; cvc->modulusSize = (int)keysize; LOG_FUNC_RETURN(p15card->card->ctx, SC_SUCCESS); } static int sc_hsm_encode_gakp_ec(struct sc_pkcs15_card *p15card, sc_cvc_t *cvc, struct sc_pkcs15_prkey_info *key_info) { struct sc_object_id ecdsaWithSHA256 = { { 0,4,0,127,0,7,2,2,2,2,3,-1 } }; struct sc_ec_parameters *ecparams = (struct sc_ec_parameters *)key_info->params.data; struct ec_curve *curve = NULL; u8 *curveoid; int curveoidlen,r; LOG_FUNC_CALLED(p15card->card->ctx); curveoid = ecparams->der.value; if ((ecparams->der.len < 3) || (*curveoid++ != 0x06)) { sc_log(p15card->card->ctx, "EC_PARAMS does not contain curve object identifier"); LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_INVALID_DATA); } curveoidlen = *curveoid++; r = sc_pkcs15emu_sc_hsm_get_curve(&curve, curveoid, curveoidlen); LOG_TEST_RET(p15card->card->ctx, r, "Unsupported named curve"); cvc->primeOrModuluslen = curve->prime.len; cvc->primeOrModulus = malloc(cvc->primeOrModuluslen); if (!cvc->primeOrModulus) { LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_OUT_OF_MEMORY); } memcpy(cvc->primeOrModulus, curve->prime.value, cvc->primeOrModuluslen); cvc->coefficientAorExponentlen = curve->coefficientA.len; cvc->coefficientAorExponent = malloc(cvc->coefficientAorExponentlen); if (!cvc->coefficientAorExponent) { LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_OUT_OF_MEMORY); } memcpy(cvc->coefficientAorExponent, curve->coefficientA.value, cvc->coefficientAorExponentlen); cvc->coefficientBlen = curve->coefficientB.len; cvc->coefficientB = malloc(cvc->coefficientBlen); if (!cvc->coefficientB) { LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_OUT_OF_MEMORY); } memcpy(cvc->coefficientB, curve->coefficientB.value, cvc->coefficientBlen); cvc->basePointGlen = curve->basePointG.len; cvc->basePointG = malloc(cvc->basePointGlen); if (!cvc->basePointG) { LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_OUT_OF_MEMORY); } memcpy(cvc->basePointG, curve->basePointG.value, cvc->basePointGlen); cvc->orderlen = curve->order.len; cvc->order = malloc(cvc->orderlen); if (!cvc->order) { LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_OUT_OF_MEMORY); } memcpy(cvc->order, curve->order.value, cvc->orderlen); cvc->cofactorlen = curve->coFactor.len; cvc->cofactor = malloc(cvc->cofactorlen); if (!cvc->cofactor) { LOG_FUNC_RETURN(p15card->card->ctx, SC_ERROR_OUT_OF_MEMORY); } memcpy(cvc->cofactor, curve->coFactor.value, cvc->cofactorlen); cvc->pukoid = ecdsaWithSHA256; LOG_FUNC_RETURN(p15card->card->ctx, SC_SUCCESS); } static int sc_hsm_generate_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *object, 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; u8 *cvcbin, *cvcpo; unsigned int cla,tag; size_t taglen, cvclen; int r; LOG_FUNC_CALLED(p15card->card->ctx); key_info->key_reference = sc_hsm_determine_free_id(p15card, KEY_PREFIX); LOG_TEST_RET(card->ctx, key_info->key_reference, "Could not determine key reference"); memset(&cvc, 0, sizeof(cvc)); strlcpy(cvc.car, "UTCA00001", sizeof cvc.car); 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: r = sc_hsm_encode_gakp_rsa(p15card, &cvc, key_info->modulus_length); break; case SC_PKCS15_TYPE_PRKEY_EC: r = sc_hsm_encode_gakp_ec(p15card, &cvc, key_info); break; default: r = SC_ERROR_NOT_IMPLEMENTED; break; } 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); LOG_TEST_RET(p15card->card->ctx, r, "Could not encode GAKP cdata"); cvcpo = cvcbin; sc_asn1_read_tag((const u8 **)&cvcpo, cvclen, &cla, &tag, &taglen); sc_asn1_read_tag((const u8 **)&cvcpo, cvclen, &cla, &tag, &taglen); sc_hsm_keyinfo.key_id = key_info->key_reference; sc_hsm_keyinfo.auth_key_id = 0; sc_hsm_keyinfo.gakprequest = cvcpo; sc_hsm_keyinfo.gakprequest_len = taglen; sc_hsm_keyinfo.gakpresponse = NULL; sc_hsm_keyinfo.gakpresponse_len = 0; r = sc_card_ctl(card, SC_CARDCTL_SC_HSM_GENERATE_KEY, &sc_hsm_keyinfo); if (r < 0) goto out; cvcpo = sc_hsm_keyinfo.gakpresponse; cvclen = sc_hsm_keyinfo.gakpresponse_len; r = sc_pkcs15emu_sc_hsm_decode_cvc(p15card, (const u8 **)&cvcpo, &cvclen, &cvc); if (r < 0) { sc_log(p15card->card->ctx, "Could not decode GAKP rdata"); r = SC_ERROR_OBJECT_NOT_VALID; goto out; } r = sc_hsm_update_ef(p15card, EE_CERTIFICATE_PREFIX, key_info->key_reference, 1, sc_hsm_keyinfo.gakpresponse, sc_hsm_keyinfo.gakpresponse_len); if (r < 0) { sc_log(p15card->card->ctx, "Could not save certificate signing request"); goto out; } if (pubkey != NULL) { r = sc_pkcs15emu_sc_hsm_get_public_key(p15card->card->ctx, &cvc, pubkey); } out: sc_pkcs15emu_sc_hsm_free_cvc(&cvc); if (cvcbin) { free(cvcbin); } if (sc_hsm_keyinfo.gakpresponse) { free(sc_hsm_keyinfo.gakpresponse); } LOG_FUNC_RETURN(p15card->card->ctx, r); } /* * Certificates with a related private key are stored in the fid range CE00 - CEFF. The * second byte in the fid matches the key id. * Certificates without a related private key (e.g. CA certificates) are stored in the fid range * CA00 - CAFF. The second byte is a free selected id. */ static int sc_hsm_emu_store_cert(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15_object *object, struct sc_pkcs15_der *data) { struct sc_pkcs15_cert_info *cert_info = (struct sc_pkcs15_cert_info *) object->data; struct sc_pkcs15_object *prkey; sc_path_t path; u8 id[2]; int r; r = sc_pkcs15_find_object_by_id(p15card, SC_PKCS15_TYPE_PRKEY, &cert_info->id , &prkey); if (r == SC_ERROR_OBJECT_NOT_FOUND) { r = sc_hsm_determine_free_id(p15card, CA_CERTIFICATE_PREFIX); LOG_TEST_RET(p15card->card->ctx, r, "Out of identifier to store certificate description"); id[0] = CA_CERTIFICATE_PREFIX; id[1] = r; } else { LOG_TEST_RET(p15card->card->ctx, r, "Error locating matching private key"); id[0] = EE_CERTIFICATE_PREFIX; id[1] = ((struct sc_pkcs15_prkey_info *)prkey->data)->key_reference; } sc_path_set(&path, SC_PATH_TYPE_FILE_ID, id, 2, 0, -1); cert_info->path = path; r = sc_hsm_update_ef(p15card, id[0], id[1], 1, data->value, data->len); return r; } static int sc_hsm_emu_delete_cert(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15_object *object) { struct sc_pkcs15_cert_info *cert_info = (struct sc_pkcs15_cert_info *) object->data; return sc_hsm_delete_ef(p15card, cert_info->path.value[0], cert_info->path.value[1]); } static int sc_hsm_emu_store_binary(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15_object *object, struct sc_pkcs15_der *data) { struct sc_pkcs15_data_info *data_info = (struct sc_pkcs15_data_info *) object->data; sc_path_t path; u8 id[2]; int r; r = sc_hsm_determine_free_id(p15card, DCOD_PREFIX); LOG_TEST_RET(p15card->card->ctx, r, "Out of identifier to store data description"); if (object->flags & SC_PKCS15_CO_FLAG_PRIVATE) { id[0] = PROT_DATA_PREFIX; } else { id[0] = DATA_PREFIX; } id[1] = r; sc_path_set(&path, SC_PATH_TYPE_FILE_ID, id, 2, 0, -1); data_info->path = path; r = sc_hsm_update_ef(p15card, id[0], id[1], 1, data->value, data->len); return r; } static int sc_hsm_emu_store_data(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15_object *object, struct sc_pkcs15_der *data, struct sc_path *path) { struct sc_context *ctx = p15card->card->ctx; int r; LOG_FUNC_CALLED(ctx); switch (object->type & SC_PKCS15_TYPE_CLASS_MASK) { case SC_PKCS15_TYPE_PRKEY: case SC_PKCS15_TYPE_PUBKEY: r = SC_SUCCESS; break; case SC_PKCS15_TYPE_CERT: r = sc_hsm_emu_store_cert(p15card, profile, object, data); break; case SC_PKCS15_TYPE_DATA_OBJECT: r = sc_hsm_emu_store_binary(p15card, profile, object, data); break; default: r = SC_ERROR_NOT_IMPLEMENTED; break; } LOG_FUNC_RETURN(ctx, r); } static int sc_hsm_emu_delete_object(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *object, const struct sc_path *path) { struct sc_context *ctx = p15card->card->ctx; int r; LOG_FUNC_CALLED(ctx); switch (object->type & SC_PKCS15_TYPE_CLASS_MASK) { case SC_PKCS15_TYPE_PRKEY: r = sc_hsm_delete_ef(p15card, KEY_PREFIX, ((struct sc_pkcs15_prkey_info *)object->data)->key_reference); break; case SC_PKCS15_TYPE_CERT: r = sc_hsm_emu_delete_cert(p15card, profile, object); break; case SC_PKCS15_TYPE_DATA_OBJECT: r = sc_delete_file(p15card->card, path); break; case SC_PKCS15_TYPE_PUBKEY: r = SC_SUCCESS; break; default: r = SC_ERROR_NOT_IMPLEMENTED; break; } LOG_FUNC_RETURN(ctx, r); } static int sc_hsm_emu_update_prkd(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *object) { struct sc_pkcs15_prkey_info *key_info = (struct sc_pkcs15_prkey_info *)object->data; u8 *buf; size_t buflen; int r; // Don't save AID in PRKD key_info->path.aid.len = 0; r = sc_pkcs15_encode_prkdf_entry(p15card->card->ctx, object, &buf, &buflen); LOG_TEST_RET(p15card->card->ctx, r, "Error encoding PRKD entry"); r = sc_hsm_update_ef(p15card, PRKD_PREFIX, key_info->key_reference, 0, buf, buflen); free(buf); return r; } static int sc_hsm_emu_update_dcod(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *object) { struct sc_pkcs15_data_info *data_info = (struct sc_pkcs15_data_info *) object->data; u8 *buf; size_t buflen; int r; r = sc_pkcs15_encode_dodf_entry(p15card->card->ctx, object, &buf, &buflen); LOG_TEST_RET(p15card->card->ctx, r, "Error encoding DCOD entry"); r = sc_hsm_update_ef(p15card, DCOD_PREFIX, data_info->path.value[data_info->path.len - 1], 0, buf, buflen); free(buf); return r; } static int sc_hsm_emu_update_cd(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *object) { struct sc_pkcs15_cert_info *cert_info = (struct sc_pkcs15_cert_info *) object->data; u8 *buf; size_t buflen; int r; if ((cert_info->path.len < 2) || ((cert_info->path.value[cert_info->path.len - 2]) != CA_CERTIFICATE_PREFIX)) { // Certificates associated with stored private keys don't get a separate CD entry return SC_SUCCESS; } r = sc_pkcs15_encode_cdf_entry(p15card->card->ctx, object, &buf, &buflen); LOG_TEST_RET(p15card->card->ctx, r, "Error encoding CD entry"); r = sc_hsm_update_ef(p15card, CD_PREFIX, cert_info->path.value[cert_info->path.len - 1], 0, buf, buflen); free(buf); return r; } static int sc_hsm_emu_delete_cd(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *object) { struct sc_pkcs15_cert_info *cert_info = (struct sc_pkcs15_cert_info *) object->data; if ((cert_info->path.len < 2) || ((cert_info->path.value[cert_info->path.len - 2]) != CA_CERTIFICATE_PREFIX)) { // Certificates associated with stored private keys don't get a separate CD entry return SC_SUCCESS; } return sc_hsm_delete_ef(p15card, CD_PREFIX, cert_info->path.value[cert_info->path.len - 1]); } static int sc_hsm_emu_update_any_df(struct sc_profile *profile, struct sc_pkcs15_card *p15card, unsigned op, struct sc_pkcs15_object *object) { struct sc_context *ctx = p15card->card->ctx; int rv = SC_ERROR_NOT_SUPPORTED; SC_FUNC_CALLED(ctx, 1); switch(op) { case SC_AC_OP_ERASE: 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); break; case SC_PKCS15_TYPE_PUBKEY: rv = SC_SUCCESS; break; case SC_PKCS15_TYPE_CERT: rv = sc_hsm_emu_delete_cd(profile, p15card, object); break; case SC_PKCS15_TYPE_DATA_OBJECT: rv = sc_hsm_delete_ef(p15card, DCOD_PREFIX, ((struct sc_pkcs15_data_info *)object->data)->path.value[1]); break; } break; case SC_AC_OP_UPDATE: case SC_AC_OP_CREATE: 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; break; case SC_PKCS15_TYPE_PRKEY: rv = sc_hsm_emu_update_prkd(profile, p15card, object); break; case SC_PKCS15_TYPE_CERT: rv = sc_hsm_emu_update_cd(profile, p15card, object); break; case SC_PKCS15_TYPE_DATA_OBJECT: rv = sc_hsm_emu_update_dcod(profile, p15card, object); break; } break; } SC_FUNC_RETURN(ctx, 1, rv); } static struct sc_pkcs15init_operations sc_pkcs15init_sc_hsm_operations = { NULL, /* erase_card */ NULL, /* init_card */ NULL, /* create_dir */ NULL, /* create_domain */ NULL, /* select_pin_reference */ NULL, /* create_pin */ NULL, /* select key reference */ sc_hsm_create_key, sc_hsm_store_key, sc_hsm_generate_key, NULL, /* encode private key */ NULL, /* encode public key */ NULL, /* finalize_card */ sc_hsm_emu_delete_object, /* delete object */ NULL, /* pkcs15init emulation update_dir */ sc_hsm_emu_update_any_df, /* pkcs15init emulation update_any_df */ NULL, /* pkcs15init emulation update_tokeninfo */ NULL, /* pkcs15init emulation write_info */ sc_hsm_emu_store_data, NULL, /* sanity_check */ }; struct sc_pkcs15init_operations * sc_pkcs15init_get_sc_hsm_ops(void) { return &sc_pkcs15init_sc_hsm_operations; } OpenSC-0.26.1/src/pkcs15init/pkcs15-setcos.c000066400000000000000000000470341474147347300203060ustar00rootroot00000000000000/* * SetOCS 4.4 specific operations for PKCS15 initialization * * Copyright (C) 2003, 2005 Zetes * * 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 "libopensc/opensc.h" #include "libopensc/cardctl.h" #include "libopensc/log.h" #include "pkcs15-init.h" #include "profile.h" #define SETCOS_MAX_PINS 7 static unsigned char SETCOS_DEFAULT_PUBKEY[] = {0x01, 0x00, 0x01}; #define SETCOS_DEFAULT_PUBKEY_LEN sizeof(SETCOS_DEFAULT_PUBKEY) static int setcos_create_pin_internal(sc_profile_t *, sc_pkcs15_card_t *, int, sc_pkcs15_auth_info_t *, const u8 *, size_t, const u8 *, size_t); static int setcos_puk_retries(sc_profile_t *profile, int pin_ref) { 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. */ sc_profile_get_pin_info(profile, SC_PKCS15INIT_SO_PIN, &auth_info); /* If pin_ref is the SO PIN, get the SO PUK info, otherwise the User PUK info */ sc_profile_get_pin_info(profile, pin_ref == auth_info.attrs.pin.reference ? SC_PKCS15INIT_SO_PUK : SC_PKCS15INIT_USER_PUK, &auth_info); if ((auth_info.tries_left < 0) || (auth_info.tries_left > 15)) return 3; /* Little extra safety */ return auth_info.tries_left; } /* * Erase the card. */ static int setcos_erase_card(sc_profile_t *profile, sc_pkcs15_card_t *p15card) { sc_path_t path; int r; /* Just delete the entire MF */ /* Select parent DF and verify PINs/key as necessary */ r = sc_pkcs15init_authenticate(profile, p15card, profile->mf_info->file, SC_AC_OP_DELETE); if (r < 0) return r == SC_ERROR_FILE_NOT_FOUND ? 0 : r; /* Empty path -> we have to to delete the current DF (= the MF) */ memset(&path, 0, sizeof(sc_path_t)); r = sc_delete_file(p15card->card, &path) ; if (r) return r; sc_free_apps(p15card->card); return 0; } /* * Create the MF and global pin file if they don't exist. */ static int setcos_init_card(sc_profile_t *profile, sc_pkcs15_card_t *p15card) { struct sc_context *ctx = p15card->card->ctx; sc_file_t *mf = profile->mf_info->file; sc_file_t *pinfile; int r; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); /* 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_log(ctx, "MF doesn't exist, creating now"); /* Fix up the file's ACLs */ r = sc_pkcs15init_fixup_file(profile, p15card, mf); LOG_TEST_RET(ctx, r, "MF fixup failed"); mf->status = SC_FILE_STATUS_CREATION; r = sc_create_file(p15card->card, mf); LOG_TEST_RET(ctx, r, "MF creation failed"); } 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); 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_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); LOG_TEST_RET(ctx, r, "Pinfile fixup failed"); /* Set life cycle state to SC_FILE_STATUS_CREATION, * which means that all ACs are ignored. */ pinfile->status = SC_FILE_STATUS_CREATION; r = sc_create_file(p15card->card, pinfile); if (r < 0) sc_file_free(pinfile); LOG_TEST_RET(ctx, r, "Pinfile creation failed"); } sc_file_free(pinfile); LOG_TEST_RET(ctx, r, "Select pinfile failed"); LOG_FUNC_RETURN(ctx, r); } /* * Create a DF */ static int setcos_create_dir(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *df) { struct sc_context *ctx = p15card->card->ctx; int r; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); r = sc_pkcs15init_fixup_file(profile, p15card, df); LOG_TEST_RET(ctx, r, "SetCOS file ACL fixup failed"); r = sc_create_file(p15card->card, df); LOG_TEST_RET(ctx, r, "SetCOS create file failed"); LOG_FUNC_RETURN(ctx, r); } /* * Select the PIN reference */ 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 = {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 != 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 != 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 SC_SUCCESS; } /* * Create a new PIN */ static int setcos_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) { 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, *tmp_pinfile = NULL; int r, ignore_ac = 0; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); 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", &tmp_pinfile); LOG_TEST_RET(ctx, r, "No 'pinfile' template in profile"); r = sc_select_file(p15card->card, &tmp_pinfile->path, &pinfile); sc_file_free(tmp_pinfile); LOG_TEST_RET(ctx, r, "Cannot select 'pinfile'"); 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) ignore_ac = 1; r = setcos_create_pin_internal(profile, p15card, ignore_ac, auth_info, pin, pin_len, puk, puk_len); /* 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); LOG_TEST_RET(ctx, r, "Cannot set 'pinfile' into the activated state"); r = sc_select_file(p15card->card, &mf->path, NULL); LOG_TEST_RET(ctx, r, "Cannot select MF"); r = sc_card_ctl(p15card->card, SC_CARDCTL_SETCOS_ACTIVATE_FILE, NULL); LOG_TEST_RET(ctx, r, "Cannot set MF into the activated state"); } LOG_FUNC_RETURN(ctx, r); } /* * Setup file struct & path: get correct template from the profile, construct full path */ static int setcos_new_file(sc_profile_t *profile, sc_card_t *card, unsigned int type, unsigned int num, /* number of objects of this type already on the card */ sc_file_t **out) { sc_file_t *file = NULL; sc_path_t *p; char name[64]; const char *tag; int r; if (type == SC_PKCS15_TYPE_PRKEY_RSA) tag = "private-key"; else if (type == SC_PKCS15_TYPE_PUBKEY_RSA) tag = "public-key"; else if ((type & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_CERT) tag = "certificate"; else if ((type & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_DATA_OBJECT) tag = "data"; else { 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_log(card->ctx, "Profile doesn't define %s", name); return SC_ERROR_NOT_SUPPORTED; } /* Auto-increment FID for next object */ 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); /* Increment FID until there's no file with such path */ r = sc_select_file(card, p, NULL); while(r == 0) { file->id++; p->value[p->len - 2] = (u8) (file->id / 256); p->value[p->len - 1] = (u8) (file->id % 256); r = sc_select_file(card, p, NULL); } *out = file; return 0; } static int setcos_encode_private_key(sc_profile_t *profile, sc_card_t *card, struct sc_pkcs15_prkey_rsa *rsa, u8 *key, size_t *keysize, int key_ref) { return 0; } static int setcos_encode_public_key(sc_profile_t *profile, sc_card_t *card, struct sc_pkcs15_prkey_rsa *rsa, u8 *key, size_t *keysize, int key_ref) { return 0; } static int setcos_create_key(sc_profile_t *profile, sc_pkcs15_card_t *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 = NULL; 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) LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Create key failed: RSA only supported"); /* Parameter check */ if ((keybits < 512) || (keybits > 1024) || (keybits & 0x7)) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Invalid key length"); 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); 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)); 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_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_pkcs15init_delete_by_path(profile, p15card, &file->path); 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_file_free(file); LOG_TEST_RET(ctx, r, "Cannot create private key file"); LOG_FUNC_RETURN(ctx, r); } /* * Store a private key */ static int setcos_store_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *object, struct sc_pkcs15_prkey *prkey) { struct sc_context *ctx = p15card->card->ctx; 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; size_t keybits = key_info->modulus_length; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); if (object->type != SC_PKCS15_TYPE_PRKEY_RSA) LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Store key failed: RSA only supported"); /* Parameter check */ if ( (keybits < 512) || (keybits > 1024) || (keybits & 0x7)) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Invalid key length"); 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); LOG_TEST_RET(ctx, r, "Cannot store 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 private key"); /* Fill in data structure */ memset(&args, 0, sizeof(args)); args.mod_len = keybits; args.op_type = OP_TYPE_STORE; args.pubexp_len = prkey->u.rsa.exponent.len * 8; args.pubexp = prkey->u.rsa.exponent.data; args.primep_len = prkey->u.rsa.p.len * 8; args.primep = prkey->u.rsa.p.data; args.primeq_len = prkey->u.rsa.q.len * 8; args.primeq = prkey->u.rsa.q.data; /* Generate/store rsa key */ r = sc_card_ctl(p15card->card, SC_CARDCTL_SETCOS_GENERATE_STORE_KEY, &args); LOG_TEST_RET(ctx, r, "Card control 'GENERATE_STORE_KEY' failed"); sc_file_free(file); LOG_FUNC_RETURN(ctx, r); } static int setcos_generate_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *object, struct sc_pkcs15_pubkey *pubkey) { struct sc_context *ctx = p15card->card->ctx; struct sc_cardctl_setcos_gen_store_key_info args; struct sc_cardctl_setcos_data_obj data_obj; struct sc_pkcs15_prkey_info *key_info = (struct sc_pkcs15_prkey_info *)object->data; int r; size_t keybits = key_info->modulus_length; unsigned char raw_pubkey[256]; struct sc_file *file = NULL; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); if (object->type != SC_PKCS15_TYPE_PRKEY_RSA) LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Generate key failed: RSA only supported"); /* Parameter check */ if ( (keybits < 512) || (keybits > 1024) || (keybits & 0x7)) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Invalid key length"); r = sc_select_file(p15card->card, &key_info->path, &file); LOG_TEST_RET(ctx, r, "Cannot store key: select key file failed"); /* Authenticate */ r = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_UPDATE); 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)); args.mod_len = keybits; args.op_type = OP_TYPE_GENERATE; args.pubexp_len = SETCOS_DEFAULT_PUBKEY_LEN * 8; args.pubexp = SETCOS_DEFAULT_PUBKEY; /* Generate/store rsa key */ r = sc_card_ctl(p15card->card, SC_CARDCTL_SETCOS_GENERATE_STORE_KEY, &args); if (r != SC_SUCCESS) { sc_file_free(file); LOG_TEST_RET(ctx, r, "Card control 'GENERATE_STORE_KEY' failed"); } /* 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.data = malloc(pubkey->u.rsa.modulus.len); pubkey->u.rsa.exponent.len = SETCOS_DEFAULT_PUBKEY_LEN; pubkey->u.rsa.exponent.data = malloc(SETCOS_DEFAULT_PUBKEY_LEN); 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_file_free(file); LOG_TEST_RET(ctx, r, "Cannot get key modulus: select key file failed"); data_obj.P1 = 0x01; data_obj.P2 = 0x01; data_obj.Data = raw_pubkey; data_obj.DataLen = sizeof(raw_pubkey); r = sc_card_ctl(p15card->card, SC_CARDCTL_SETCOS_GETDATA, &data_obj); 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_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); 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); } else { sc_file_free(file); } return r; } /* * Create a new PIN */ static int setcos_create_pin_internal(sc_profile_t *profile, sc_pkcs15_card_t *p15card, int ignore_ac, sc_pkcs15_auth_info_t *auth_info, const u8 *pin, size_t pin_len, const u8 *puk, size_t puk_len) { struct sc_context *ctx = p15card->card->ctx; u8 data[32]; int r; struct sc_cardctl_setcos_data_obj data_obj; sc_file_t *pinfile = NULL; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return SC_ERROR_OBJECT_NOT_VALID; if (auth_info->attrs.pin.reference >= SETCOS_MAX_PINS) return SC_ERROR_INVALID_ARGUMENTS; if (pin == NULL || puk == NULL || pin_len < 4 || puk_len < 4) return SC_ERROR_INVALID_PIN_LENGTH; /* Verify required access rights if needed (i.e. if the * pin file isn't in the CREATE life cycle state). */ if (!ignore_ac) { r = sc_profile_get_file(profile, "pinfile", &pinfile); if (r >= 0) r = sc_pkcs15init_authenticate(profile, p15card, pinfile, SC_AC_OP_UPDATE); sc_file_free(pinfile); if (r < 0) return r; } /* Make command to add a pin-record */ data_obj.P1 = 01; data_obj.P2 = 01; /* setcos pin number */ data[0] = auth_info->attrs.pin.reference; memset(&data[1], auth_info->attrs.pin.pad_char, 16); /* padding */ memcpy(&data[1], (u8 *)pin, pin_len); /* copy pin*/ memcpy(&data[9], (u8 *)puk, puk_len); /* copy puk */ data[17] = auth_info->tries_left & 0x0F; data[18] = auth_info->tries_left & 0x0F; /* 0xF0: unlimited unblock tries */ data[19] = 0xF0 | setcos_puk_retries(profile, auth_info->attrs.pin.reference); /* Allow an unlimited number of signatures after a pin verification. * If set to 1 or so, we would have a UserConsent PIN. */ data[20] = 0x00; if (auth_info->attrs.pin.type == 0) data[21] = 0x01; /* BCD */ else data[21] = 0x00; /* ASCII */ if ((auth_info->attrs.pin.flags & 0x010) == 0) /* test for initial pin */ data[21] |= 0x80; data[22] = 0x00; /* not used */ data[23] = 0x00; /* not used */ data_obj.Data = data; data_obj.DataLen = 24; r = sc_card_ctl(p15card->card, SC_CARDCTL_SETCOS_PUTDATA, &data_obj); SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r); } static struct sc_pkcs15init_operations sc_pkcs15init_setcos_operations = { setcos_erase_card, /* erase_card */ setcos_init_card, /* init_card */ setcos_create_dir, /* create_dir */ NULL, /* create_domain */ setcos_select_pin_reference, /* select_pin_reference */ setcos_create_pin, /* create_pin */ NULL, /* select_key_reference */ setcos_create_key, /* create_key */ setcos_store_key, /* store_key */ setcos_generate_key, /* generate_key */ setcos_encode_private_key, /* encode_private_key */ setcos_encode_public_key, /* encode_public_key */ NULL, /* finalize_card */ NULL, /* delete_object */ NULL, /* emu_update_dir */ NULL, /* emu_update_any_df */ NULL, /* emu_update_tokeninfo */ NULL, /* emu_write_info */ NULL, /* emu_store_data */ NULL /* sanity_check */ }; struct sc_pkcs15init_operations * sc_pkcs15init_get_setcos_ops(void) { return &sc_pkcs15init_setcos_operations; } OpenSC-0.26.1/src/pkcs15init/pkcs15-starcos.c000066400000000000000000000662401474147347300204640ustar00rootroot00000000000000/* * Starcos SPK 2.3 specific operation for PKCS15 initialization * * Copyright (C) 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include #include #include #include #include #include "libopensc/log.h" #include "libopensc/opensc.h" #include "libopensc/cardctl.h" #include "pkcs15-init.h" #include "profile.h" #define STARCOS_AC_NEVER 0x5f #define STARCOS_AC_ALWAYS 0x9f #define STARCOS_SOPIN_GID 0x01 #define STARCOS_SOPIN_STATE 0x01 #define STARCOS_SOPIN_GAC 0x01 #define STARCOS_SOPIN_LID 0x81 #define STARCOS_SOPIN_LAC 0x11; static int starcos_finalize_card(sc_card_t *card); static int starcos_erase_card(struct sc_profile *pro, sc_pkcs15_card_t *p15card) { return sc_card_ctl(p15card->card, SC_CARDCTL_ERASE_CARD, NULL); } static u8 get_so_ac(const sc_file_t *file, unsigned int op, const sc_pkcs15_auth_info_t *auth, unsigned int def, unsigned int need_global) { int is_global = 1; const sc_acl_entry_t *acl; if (auth->attrs.pin.flags & SC_PKCS15_PIN_FLAG_LOCAL) is_global = 0; if (!is_global && need_global) return def & 0xff; acl = sc_file_get_acl_entry(file, op); if (acl->method == SC_AC_NONE) return STARCOS_AC_ALWAYS; else if (acl->method == SC_AC_NEVER) return STARCOS_AC_NEVER; else if (acl->method == SC_AC_SYMBOLIC) { if (is_global) return STARCOS_SOPIN_GAC; else return STARCOS_SOPIN_LAC; } else return def; } static int starcos_init_card(sc_profile_t *profile, sc_pkcs15_card_t *p15card) { struct sc_card *card = p15card->card; static const u8 key[] = {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08}; int ret; sc_starcos_create_data mf_data, ipf_data; 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 = {0}; /* test if we already have a MF */ memset(&tpath, 0, sizeof(sc_path_t)); tpath.value[0] = 0x3f; tpath.value[1] = 0x00; tpath.len = 2; tpath.type = SC_PATH_TYPE_PATH; ret = sc_select_file(card, &tpath, NULL); if (ret == SC_SUCCESS) /* we already have a MF => return OK */ return ret; sc_profile_get_pin_info(profile, SC_PKCS15INIT_SO_PIN, &sopin); /* get mf profile */ ret = sc_profile_get_file(profile, "MF", &mf_file); if (ret < 0) return ret; /* get size of the isf */ ret = sc_profile_get_file(profile, "mf_isf", &isf_file); if (ret < 0) { sc_file_free(mf_file); return ret; } mf_data.type = SC_STARCOS_MF_DATA; memcpy(p, key, 8); p += 8; *p++ = (mf_file->size >> 8) & 0xff; *p++ = mf_file->size & 0xff; *p++ = (isf_file->size >> 8) & 0xff; *p++ = isf_file->size & 0xff; /* AC CREATE EF */ *p++ = get_so_ac(mf_file, SC_AC_OP_CREATE, &sopin, STARCOS_AC_ALWAYS, 1); /* AC CREATE KEY */ *p++ = get_so_ac(isf_file, SC_AC_OP_WRITE, &sopin, STARCOS_AC_NEVER, 1); /* AC CREATE DF */ *p++ = get_so_ac(mf_file, SC_AC_OP_CREATE, &sopin, STARCOS_AC_ALWAYS, 1); /* AC REGISTER DF */ *p++ = get_so_ac(mf_file, SC_AC_OP_CREATE, &sopin, STARCOS_AC_ALWAYS, 1); *p++ = 0x00; /* SM CR: no */ *p++ = 0x00; /* SM EF: no */ *p = 0x00; /* SM ISF: no */ sc_file_free(mf_file); sc_file_free(isf_file); /* call CREATE MF */ ret = sc_card_ctl(card, SC_CARDCTL_STARCOS_CREATE_FILE, &mf_data); if (ret != SC_SUCCESS) return ret; /* create IPF */ /* get size of the ipf */ ret = sc_profile_get_file(profile, "mf_ipf", &ipf_file); if (ret < 0) return ret; ipf_data.type = SC_STARCOS_EF_DATA; p = ipf_data.data.ef.header; *p++ = (ipf_file->id >> 8) & 0xff; *p++ = ipf_file->id & 0xff; *p++ = STARCOS_AC_ALWAYS; /* AC READ: always */ /* AC WRITE IPF */ *p++ = get_so_ac(ipf_file,SC_AC_OP_CREATE, &sopin, STARCOS_AC_ALWAYS, 1); *p++ = STARCOS_AC_NEVER; /* AC ERASE */ *p++ = STARCOS_AC_NEVER; /* AC LOCK */ *p++ = STARCOS_AC_NEVER; /* AC UNLOCK */ *p++ = STARCOS_AC_NEVER; /* AC INCREASE */ *p++ = STARCOS_AC_NEVER; /* AC_DECREASE */ *p++ = STARCOS_AC_NEVER; /* RFU */ *p++ = STARCOS_AC_NEVER; /* RFU */ *p++ = 0x00; /* SM */ *p++ = 0x00; /* SID */ *p++ = 0xA1; /* IPF */ *p++ = (ipf_file->size >> 8) & 0xff; *p = ipf_file->size & 0xff; ret = sc_card_ctl(card, SC_CARDCTL_STARCOS_CREATE_FILE, &ipf_data); if (ret != SC_SUCCESS) { sc_file_free(ipf_file); return ret; } /* init IPF */ ret = sc_select_file(card, &ipf_file->path, NULL); sc_file_free(ipf_file); if (ret < 0) return ret; ret = sc_update_binary(card, 0, &tmp, 1, 0); if (ret < 0) return ret; return SC_SUCCESS; } static int starcos_create_dir(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_file_t *df) { struct sc_card *card = p15card->card; int ret; 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 = {0}; sc_profile_get_pin_info(profile, SC_PKCS15INIT_SO_PIN, &sopin); /* get p15_isf profile */ ret = sc_profile_get_file(profile, "p15_isf", &isf_file); if (ret < 0) return ret; df_data.type = SC_STARCOS_DF_DATA; memset(p, 0, 25); *p++ = (df->id >> 8) & 0xff; *p++ = df->id & 0xff; *p++ = df->namelen & 0xff; memcpy(p, df->name, (u8) df->namelen); p += 16; *p++ = (isf_file->size >> 8) & 0xff; *p++ = isf_file->size & 0xff; /* AC CREATE EF */ *p++ = get_so_ac(df, SC_AC_OP_CREATE, &sopin, STARCOS_AC_ALWAYS, 0); /* AC CREATE KEY */ *p++ = get_so_ac(isf_file, SC_AC_OP_WRITE, &sopin, STARCOS_AC_NEVER, 0); *p++ = 0x00; /* SM EF: no */ *p = 0x00; /* SM ISF: no */ df_data.data.df.size[0] = (df->size >> 8) & 0xff; df_data.data.df.size[1] = df->size & 0xff; sc_file_free(isf_file); /* call CREATE DF */ ret = sc_card_ctl(card, SC_CARDCTL_STARCOS_CREATE_FILE, &df_data); if (ret != SC_SUCCESS) return ret; /* create IPF */ ret = sc_select_file(card, &df->path, NULL); if (ret != SC_SUCCESS) return ret; ret = sc_profile_get_file(profile, "p15_ipf", &ipf_file); if (ret < 0) return ret; ipf_data.type = SC_STARCOS_EF_DATA; p = ipf_data.data.ef.header; *p++ = (ipf_file->id >> 8) & 0xff; *p++ = ipf_file->id & 0xff; *p++ = STARCOS_AC_ALWAYS; /* AC READ */ /* AC WRITE IPF */ *p++ = get_so_ac(ipf_file, SC_AC_OP_CREATE, &sopin, STARCOS_AC_ALWAYS, 0); *p++ = STARCOS_AC_NEVER; /* AC ERASE */ *p++ = STARCOS_AC_NEVER; /* AC LOCK */ *p++ = STARCOS_AC_NEVER; /* AC UNLOCK */ *p++ = STARCOS_AC_NEVER; /* AC INCREASE */ *p++ = STARCOS_AC_NEVER; /* AC_DECREASE */ *p++ = STARCOS_AC_NEVER; /* RFU */ *p++ = STARCOS_AC_NEVER; /* RFU */ *p++ = 0x00; /* SM */ *p++ = 0x00; /* SID */ *p++ = 0xA1; /* IPF */ *p++ = (ipf_file->size >> 8) & 0xff; *p = ipf_file->size & 0xff; ret = sc_card_ctl(card, SC_CARDCTL_STARCOS_CREATE_FILE, &ipf_data); if (ret != SC_SUCCESS) { sc_file_free(ipf_file); return ret; } /* init IPF */ ret = sc_select_file(card, &ipf_file->path, NULL); sc_file_free(ipf_file); if (ret < 0) return ret; ret = sc_update_binary(card, 0, &tmp, 1, 0); if (ret < 0) return ret; return SC_SUCCESS; } static int have_onepin(sc_profile_t *profile) { sc_pkcs15_auth_info_t sopin = {0}; sc_profile_get_pin_info(profile, SC_PKCS15INIT_SO_PIN, &sopin); if (!(sopin.attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN)) return 1; else return 0; } /* range of possible key ids for pins (note: the key id of the puk * is the key id of the pin plus one) */ #define STARCOS_MIN_LPIN_ID 0x83 #define STARCOS_MAX_LPIN_ID 0x8f #define STARCOS_MIN_GPIN_ID 0x03 #define STARCOS_MAX_GPIN_ID 0x0f static int starcos_pin_reference(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_auth_info_t *auth_info) { int tmp; if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return SC_ERROR_OBJECT_NOT_VALID; tmp = auth_info->attrs.pin.reference; if (have_onepin(profile)) { /* we have the onepin profile */ auth_info->attrs.pin.reference = STARCOS_SOPIN_GID; return SC_SUCCESS; } if (auth_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_LOCAL) { /* use local KID */ /* SO-pin */ if (auth_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN) tmp = STARCOS_SOPIN_LID; else { if (tmp < STARCOS_MIN_LPIN_ID) tmp = STARCOS_MIN_LPIN_ID; if (!(tmp & 0x01)) /* odd KIDs for PINs and even KIDs for PUKs */ tmp++; if (tmp > STARCOS_MAX_LPIN_ID) return SC_ERROR_TOO_MANY_OBJECTS; } } else { /* use global KID */ /* SO-pin */ if (auth_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN) tmp = STARCOS_SOPIN_GID; else { if (tmp < STARCOS_MIN_GPIN_ID) tmp = STARCOS_MIN_GPIN_ID; if (!(tmp & 0x01)) /* odd KIDs for PINs and even KIDs for PUKs */ tmp++; if (tmp > STARCOS_MAX_GPIN_ID) return SC_ERROR_TOO_MANY_OBJECTS; } } auth_info->attrs.pin.reference = tmp; return SC_SUCCESS; } /* About STARCOS_PINID2STATE * Starcos SPK 2.3 uses a state machine to control the access * to files or keys. This means that the access to a certain * object is granted if the current state (of either the current * DF or the MF) is =, <, >= or != a specified state (see * Starcos S 2.1 manual). To map the pkcs15 access control model *(one object is protected by one pin etc.) to the Starcos S 2.1 * model the following approach is used: * the pin with the key id 3 (or 0x81) sets the global (or local) * state to 15 (note: 16 is the lowest initial state). * the pin with the key id 4 (or 0x82) is reserved for the PUK * the pin with the key id 5 (or 0x83) sets the global (or local) * state to 14. * ... * Note: the key id 1 and 2 (or local 0x81 and 0x82) is used for * the 'SO-pin' which sets the state to 0x01. * XXX: some card operations, like terminate card usage are only * possible in state 0x00 * * Nils */ #define STARCOS_PINID2STATE(a) (((a) == STARCOS_SOPIN_GID) ? STARCOS_SOPIN_STATE : (0x0f - ((0x0f & (a)) >> 1))) static int starcos_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) { struct sc_card *card = p15card->card; int r, is_local, pin_id, tmp, need_finalize = 0; size_t akd; sc_file_t *tfile; const sc_acl_entry_t *acl_entry; sc_pkcs15_auth_info_t *auth_info = (sc_pkcs15_auth_info_t *) pin_obj->data; sc_starcos_wkey_data pin_d, puk_d; u8 tpin[8]; if (!pin || !pin_len || pin_len > 8) return SC_ERROR_INVALID_ARGUMENTS; if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return SC_ERROR_OBJECT_NOT_VALID; is_local = 0x80 & auth_info->attrs.pin.reference; if (is_local) r = sc_select_file(card, &df->path, NULL); else r = sc_select_file(card, &profile->mf_info->file->path, NULL); if (r < 0) return r; /* get and verify sopin if necessary */ r = sc_profile_get_file(profile, "p15_isf", &tfile); if (r < 0) return r; acl_entry = sc_file_get_acl_entry(tfile, SC_AC_OP_WRITE); if (acl_entry->method != SC_AC_NONE) { if ((auth_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN) || have_onepin(profile)) need_finalize = 1; else r = sc_pkcs15init_authenticate(profile, p15card, tfile, SC_AC_OP_WRITE); } sc_file_free(tfile); if (r < 0) return r; /* pad pin with 0 */ memset(tpin, 0, 8); memcpy(tpin, pin, pin_len); /* write PIN */ tmp = auth_info->tries_left; pin_id = auth_info->attrs.pin.reference; pin_d.mode = 0; /* install */ pin_d.kid = (u8) pin_id; pin_d.key = tpin; pin_d.key_len = 8; pin_d.key_header[0] = pin_d.kid; pin_d.key_header[1] = 0; pin_d.key_header[2] = 8; pin_d.key_header[3] = STARCOS_AC_ALWAYS; if (auth_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN) pin_d.key_header[4] = STARCOS_SOPIN_STATE; else pin_d.key_header[4] = STARCOS_PINID2STATE(pin_id); pin_d.key_header[5] = STARCOS_AC_ALWAYS; pin_d.key_header[6] = ((0x0f & tmp) << 4) | (0x0f & tmp); pin_d.key_header[7] = 0x00; pin_d.key_header[8] = 0x00; akd = auth_info->attrs.pin.min_length; if (akd < 4) akd = 4; if (akd > 8) akd = 8; akd--; akd |= 0x08; pin_d.key_header[9] = akd; /* AKD: standard + every char != 0 + * pin min length */ pin_d.key_header[10] = 0x00; /* never allow WRITE KEY */ pin_d.key_header[11] = 0x81; /* key attribute: akd + pin */ /* create/write PIN */ r = sc_card_ctl(card, SC_CARDCTL_STARCOS_WRITE_KEY, &pin_d); if (r != SC_SUCCESS) return r; if (puk && puk_len) { sc_pkcs15_auth_info_t puk_info = {0}; if (puk_len > 8) return SC_ERROR_INVALID_ARGUMENTS; memset(tpin, 0, 8); memcpy(tpin, puk, puk_len); sc_profile_get_pin_info(profile, SC_PKCS15INIT_USER_PUK, &puk_info); tmp = puk_info.tries_left; puk_d.mode = 0; /* install */ puk_d.kid = (u8) pin_id + 1; puk_d.key = tpin; puk_d.key_len = 8; puk_d.key_header[0] = puk_d.kid; puk_d.key_header[1] = 0; puk_d.key_header[2] = 8; puk_d.key_header[3] = STARCOS_AC_ALWAYS; puk_d.key_header[4] = ((pin_id & 0x1f) << 3) | 0x05; puk_d.key_header[5] = 0x01; puk_d.key_header[6] = ((0x0f & tmp) << 4) | (0x0f & tmp); puk_d.key_header[7] = 0x0; puk_d.key_header[8] = 0x0; puk_d.key_header[9] = 0x0; puk_d.key_header[10] = 0x00; puk_d.key_header[11] = 0x02; /* create/write PUK */ r = sc_card_ctl(card, SC_CARDCTL_STARCOS_WRITE_KEY, &puk_d); if (r != SC_SUCCESS) return r; } /* in case of a global pin: write dummy entry in df isf */ if (!is_local) { r = sc_select_file(card, &df->path, NULL); if (r < 0) return r; pin_d.key = NULL; pin_d.key_len = 0; pin_d.key_header[1] = 0; pin_d.key_header[2] = 0; /* create/write dummy PIN */ r = sc_card_ctl(card, SC_CARDCTL_STARCOS_WRITE_KEY, &pin_d); if (r != SC_SUCCESS) return r; } /* in case of a SOPIN: if AC WRITE KEY is protected by the * SOPIN, call starcos_finalize_card to activate the ACs */ if (need_finalize) r = starcos_finalize_card(card); return r; } /* range of possible key ids for private keys */ #define STARCOS_MIN_LPKEY_ID 0x91 #define STARCOS_MAX_LPKEY_ID 0x9f #define STARCOS_MIN_GPKEY_ID 0x11 #define STARCOS_MAX_GPKEY_ID 0x1f static int starcos_key_reference(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_prkey_info_t *prkey) { /* use (local) KIDs 0x91-0x9f for private rsa keys */ if (prkey->key_reference < STARCOS_MIN_LPKEY_ID) prkey->key_reference = STARCOS_MIN_LPKEY_ID; if (prkey->key_reference > STARCOS_MAX_LPKEY_ID) return SC_ERROR_TOO_MANY_OBJECTS; return SC_SUCCESS; } #define STARCOS_MAX_PR_KEYSIZE 370 static int starcos_encode_prkey(struct sc_pkcs15_prkey_rsa *rsa, u8 *buf) { size_t i = 0; u8 *p = buf; /* clear key buffer */ memset(buf, 0, STARCOS_MAX_PR_KEYSIZE); if (rsa->p.len && rsa->q.len && rsa->dmp1.len && rsa->dmq1.len && rsa->iqmp.len) { /* CRT RSA key */ /* 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; /* key format list */ *p++ = 0x0c; *p++ = 0x91; *p++ = (u8) rsa->p.len; *p++ = 0x92; *p++ = (u8) rsa->q.len; *p++ = 0x94; *p++ = (u8) rsa->dmp1.len + 16; *p++ = 0x95; *p++ = (u8) rsa->dmq1.len + 16; *p++ = 0x97; *p++ = (u8) rsa->p.len; *p++ = 0x00; *p++ = (u8) i; /* copy key components */ for (i = rsa->q.len; i != 0; i--) *p++ = rsa->q.data[i - 1]; for (i = rsa->p.len; i != 0; i--) *p++ = rsa->p.data[i - 1]; for (i = 16; i != 0; i--) *p++ = 0x00; for (i = rsa->dmp1.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--) *p++ = rsa->dmp1.data[i - 1]; for (i = rsa->iqmp.len; i != 0; i--) *p++ = rsa->iqmp.data[i - 1]; for (i = rsa->p.len - rsa->iqmp.len; i != 0; i--) *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; /* key format list */ *p++ = 6; *p++ = 0x90; *p++ = (u8) rsa->modulus.len; *p++ = 0x93; *p++ = (u8) rsa->d.len + 16; *p++ = 0x00; *p++ = (u8) i; /* copy key components */ for (i = rsa->modulus.len; i != 0; i--) *p++ = rsa->modulus.data[i - 1]; for (i = 16; i != 0; i--) *p++ = 0x00; for (i = rsa->d.len; i != 0; i--) *p++ = rsa->d.data[i - 1]; } else return SC_ERROR_INTERNAL; return SC_SUCCESS; } /* XXX the whole IPF stuff doesn't really work very well */ /** starcos_ipf_get_lastpos * returns the offset to the first byte after the last key */ static size_t starcos_ipf_get_lastpos(u8 *ipf, size_t ipf_len) { size_t num_keys, tmp; u8 *p = ipf; if (!ipf || ipf_len < 13) return 0; num_keys = *p++; /* the first bytes contains the number of keys*/ if (num_keys == 0xff) num_keys = 0; if (!num_keys) return 1; while (num_keys--) { size_t offset = p - ipf; /* note: p > ipf */ /* get offset to the next key header */ tmp = 12 + (p[1] << 8) + p[2]; if (tmp + offset > ipf_len) return 0; p += tmp; } return p - ipf; } static int starcos_encode_pukey(struct sc_pkcs15_prkey_rsa *rsa, u8 *buf, sc_pkcs15_prkey_info_t *kinfo) { size_t i = 0; u8 *p = buf; /* if rsa == NULL return key header for key generation */ if (!rsa) { if (!buf) /* if buf == NULL return length of the encoded key */ return 12 + (int)(kinfo->modulus_length >> 3); *p++ = 0x06; /* length key header */ *p++ = 0x01; /* CHA byte */ *p++ = 0x01; *p++ = 0x10; /* RSA: n */ *p++ = (kinfo->modulus_length >> 3) & 0xff; *p++ = 0x13; /* RSA: e */ *p++ = 0x04; *p = (u8) kinfo->key_reference; /* CHA byte */ } else { /* encode normal public key */ int mod_len = (int)rsa->modulus.len & 0xff, exp_len = (int)rsa->exponent.len & 0xff; if (!buf) return 8 + mod_len + exp_len + 1; *p++ = 0x06; /* length key header */ *p++ = 0x01; /* CHA byte */ *p++ = 0x01; *p++ = 0x10; /* RSA: n */ *p++ = mod_len; *p++ = 0x13; /* RSA: e */ *p++ = exp_len + 1; *p++ = (u8) kinfo->key_reference; /* CHA byte */ /* copy modulus */ for (i = mod_len; i != 0; i--) *p++ = rsa->modulus.data[i - 1]; /* copy exponent */ for (i = exp_len; i != 0; i--) *p++ = rsa->exponent.data[i - 1]; *p = 0x00; } return SC_SUCCESS; } static int starcos_write_pukey(sc_profile_t *profile, sc_card_t *card, struct sc_pkcs15_prkey_rsa *rsa, sc_pkcs15_prkey_info_t *kinfo) { int r; size_t len, keylen, endpos; u8 *buf, key[280], *p, num_keys; sc_file_t *tfile = NULL; sc_path_t tpath; /* get ipf profile */ tpath = kinfo->path; r = sc_profile_get_file_in(profile, &tpath, "p15_ipf", &tfile); if (r < 0) return r; tpath = tfile->path; sc_file_free(tfile); tfile = NULL; r = sc_select_file(card, &tpath, &tfile); if (r != SC_SUCCESS) /* unable to select ipf */ 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) { 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); if (!keylen) { free(buf); return SC_ERROR_INTERNAL; } p = key; *p++ = (u8) kinfo->key_reference; *p++ = (keylen >> 8) & 0xff; *p++ = keylen & 0xff; *p++ = STARCOS_AC_ALWAYS; /* AC WRITE etc XXX */ *p++ = 0x0f; *p++ = 0; *p++ = 0x09; /* ALGO XXX */ *p++ = 0x4a; /* AKD XXX */ *p++ = ((keylen >> 8) & 0xff) | 0x80; *p++ = keylen & 0xff; r = starcos_encode_pukey(rsa, p, kinfo); if (r != SC_SUCCESS) { free(buf); return SC_ERROR_INTERNAL; } p += keylen; *p++ = 0x04; /* CPI */ *p = (u8) kinfo->key_reference; /* CHA */ /* updated IPF (XXX: currently append only) */ num_keys++; r = sc_update_binary(card, 0, &num_keys, 1, 0); if (r < 0) { free(buf); return r; } endpos = starcos_ipf_get_lastpos(buf, len); free(buf); 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, sc_pkcs15_object_t *obj) { struct sc_card *card = p15card->card; int r, pin_id; u8 akd = 0, state; sc_file_t *tfile; const sc_acl_entry_t *acl_entry; sc_pkcs15_prkey_info_t *kinfo = (sc_pkcs15_prkey_info_t *)obj->data; sc_starcos_wkey_data tkey; /* get and verify sopin if necessary */ r = sc_profile_get_file(profile, "p15_isf", &tfile); if (r < 0) return r; acl_entry = sc_file_get_acl_entry(tfile, SC_AC_OP_WRITE); if (acl_entry->method != SC_AC_NONE) { r = sc_pkcs15init_authenticate(profile, p15card, tfile, SC_AC_OP_WRITE); } else { r = sc_select_file(card, &tfile->path, NULL); } sc_file_free(tfile); if (r < 0) return r; /* create sc_starcos_wkey_data */ tkey.mode = 0x00; /* install new key */ tkey.kid = (u8) kinfo->key_reference; tkey.key_header[0] = (u8) kinfo->key_reference; tkey.key_header[1] = (STARCOS_MAX_PR_KEYSIZE >> 8) & 0xff; tkey.key_header[2] = STARCOS_MAX_PR_KEYSIZE & 0xff; pin_id = sc_pkcs15init_get_pin_reference(p15card, profile, SC_AC_SYMBOLIC, SC_PKCS15INIT_USER_PIN); if (pin_id < 0) state = STARCOS_AC_ALWAYS; else { state = STARCOS_PINID2STATE(pin_id); /* get the necessary state */ state |= pin_id & 0x80 ? 0x10 : 0x00; /* local vs. global key id */ } tkey.key_header[3] = state; /* AC to access key */ if (obj->user_consent) tkey.key_header[4] = 0x0f; /* do state transition */ else tkey.key_header[4] = 0x8f; /* no state transition */ 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; if (kinfo->usage & SC_PKCS15_PRKEY_USAGE_NONREPUDIATION) akd |= 0x10; if (kinfo->usage & SC_PKCS15_PRKEY_USAGE_SIGN) akd |= 0x31; /* allow DS, IA and PKCS11 */ if (kinfo->usage & SC_PKCS15_PRKEY_USAGE_SIGNRECOVER) akd |= 0x31; /* allow DS, IA and PKCS11 */ if (kinfo->usage & SC_PKCS15_PRKEY_USAGE_DECRYPT || kinfo->usage & SC_PKCS15_PRKEY_USAGE_UNWRAP) akd |= 0x02; tkey.key_header[9] = akd; tkey.key_header[10] = 0x03; tkey.key_header[11] = 0xa0; tkey.key = NULL; tkey.key_len = 0; return sc_card_ctl(card, SC_CARDCTL_STARCOS_WRITE_KEY, &tkey); } static int starcos_store_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *obj, sc_pkcs15_prkey_t *key) { int r; u8 key_buf[STARCOS_MAX_PR_KEYSIZE]; sc_pkcs15_prkey_info_t *kinfo = (sc_pkcs15_prkey_info_t *) obj->data; const sc_acl_entry_t *acl_entry; sc_file_t *tfile; struct sc_pkcs15_prkey_rsa *rsa = &key->u.rsa; sc_starcos_wkey_data tkey; if (key->algorithm != SC_ALGORITHM_RSA) /* ignore non-RSA keys */ return SC_ERROR_INVALID_ARGUMENTS; /* create sc_starcos_wkey_data */ if (starcos_encode_prkey(rsa, key_buf)) return SC_ERROR_INTERNAL; /* get and verify sopin if necessary */ r = sc_profile_get_file(profile, "p15_isf", &tfile); if (r < 0) return r; acl_entry = sc_file_get_acl_entry(tfile, SC_AC_OP_WRITE); if (acl_entry->method != SC_AC_NONE) { r = sc_pkcs15init_authenticate(profile, p15card, tfile, SC_AC_OP_WRITE); } sc_file_free(tfile); if (r < 0) return r; tkey.mode = 0x01; /* update key */ tkey.kid = (u8) kinfo->key_reference; tkey.key = key_buf; tkey.key_len = STARCOS_MAX_PR_KEYSIZE; r = sc_card_ctl(p15card->card, SC_CARDCTL_STARCOS_WRITE_KEY, &tkey); if (r != SC_SUCCESS) return r; /* store public key in the IPF */ return starcos_write_pukey(profile, p15card->card, rsa, kinfo); } static int starcos_generate_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *obj, sc_pkcs15_pubkey_t *pubkey) { int r; const sc_acl_entry_t *acl_entry; sc_file_t *tfile; sc_starcos_gen_key_data gendat; sc_pkcs15_prkey_info_t *kinfo = (sc_pkcs15_prkey_info_t *) obj->data; if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) return SC_ERROR_NOT_SUPPORTED; /* get and verify sopin if necessary */ r = sc_profile_get_file(profile, "p15_isf", &tfile); if (r < 0) return r; acl_entry = sc_file_get_acl_entry(tfile, SC_AC_OP_WRITE); if (acl_entry->method != SC_AC_NONE) { r = sc_pkcs15init_authenticate(profile, p15card, tfile, SC_AC_OP_WRITE); } sc_file_free(tfile); if (r < 0) return r; /* XXX It would be better to write the public key header * in the IPF when the private key header is created, but * as we don't know the size of the exponent at this time * we would waste space. */ /* create (empty) public key entry */ r = starcos_write_pukey(profile, p15card->card, NULL, kinfo); if (r < 0) return r; /* 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(p15card->card, SC_CARDCTL_STARCOS_GENERATE_KEY, &gendat); if (r != SC_SUCCESS) return r; /* get the modulus via READ PUBLIC KEY */ if (pubkey) { u8 *buf; struct sc_pkcs15_pubkey_rsa *rsa = &pubkey->u.rsa; /* set the modulus */ rsa->modulus.data = gendat.modulus; rsa->modulus.len = kinfo->modulus_length >> 3; /* set the exponent (always 0x10001) */ buf = malloc(3); if (!buf) return SC_ERROR_OUT_OF_MEMORY; buf[0] = 0x01; buf[1] = 0x00; buf[2] = 0x01; rsa->exponent.data = buf; rsa->exponent.len = 3; pubkey->algorithm = SC_ALGORITHM_RSA; } else /* free public key */ free(gendat.modulus); return SC_SUCCESS; } static int starcos_finalize_card(sc_card_t *card) { int r; sc_file_t tfile; sc_path_t tpath; /* SELECT FILE MF */ sc_format_path("3F00", &tpath); r = sc_select_file(card, &tpath, NULL); if (r < 0) return r; /* call CREATE END for the MF (ignore errors) */ tfile.type = SC_FILE_TYPE_DF; tfile.id = 0x3f00; r = sc_card_ctl(card, SC_CARDCTL_STARCOS_CREATE_END, &tfile); if (r < 0) 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; r = sc_card_ctl(card, SC_CARDCTL_STARCOS_CREATE_END, &tfile); if (r == SC_ERROR_NOT_ALLOWED) /* card is already finalized */ return SC_SUCCESS; return r; } static struct sc_pkcs15init_operations sc_pkcs15init_starcos_operations = { starcos_erase_card, starcos_init_card, starcos_create_dir, NULL, /* create_domain */ starcos_pin_reference, starcos_create_pin, starcos_key_reference, starcos_create_key, starcos_store_key, starcos_generate_key, NULL, NULL, /* encode private/public key */ starcos_finalize_card, NULL, /* delete_object */ NULL, NULL, NULL, NULL, NULL, /* pkcs15init emulation */ NULL /* sanity_check */ }; struct sc_pkcs15init_operations *sc_pkcs15init_get_starcos_ops(void) { return &sc_pkcs15init_starcos_operations; } OpenSC-0.26.1/src/pkcs15init/pkcs15.profile000066400000000000000000000075651474147347300202330ustar00rootroot00000000000000# # PKCS15 profile, generic information. # This profile is loaded before any card specific profile. # cardinfo { label = "OpenSC Card"; manufacturer = "OpenSC Project"; min-pin-length = 4; # max length should be overridden in the per-card profile max-pin-length = 8; } # # 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; # Method to calculate ID of the crypto objects # 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 value: 'native' pkcs15-id-style = mozilla; } # Default settings. # This option block will always be processed. option default { macros { protected = *=$SOPIN, READ=NONE; unprotected = *=NONE; so-pin-flags = local, initialized, needs-padding, soPin; so-min-pin-length = 6; so-pin-attempts = 2; so-auth-id = 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; } } # This option sets up the card so that a single # user PIN protects all files option onepin { macros { protected = *=$PIN, READ=NONE; unprotected = *=NONE; so-pin-flags = local, initialized, needs-padding; so-min-pin-length = 4; so-pin-attempts = 3; so-auth-id = 1; so-puk-attempts = 7; so-min-puk-length = 4; } } # This option is for cards with very little memory. # It sets the size of various PKCS15 directory files # to 128 or 256, respectively. option small { macros { odf-size = 128; aodf-size = 128; cdf-size = 256; prkdf-size = 128; pukdf-size = 128; dodf-size = 128; } } # This option tells pkcs15-init to use the direct option # when storing certificates on the card (i.e. put the # certificates into the CDF itself, rather than a # separate file) option direct-cert { pkcs15 { direct-certificates = yes; encode-df-length = yes; } macros { cdf-size = 3192; } } # 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 { attempts = 3; flags = local, initialized, needs-padding; } PIN user-puk { attempts = 7; } PIN so-pin { auth-id = $so-auth-id; attempts = $so-pin-attempts; min-length = $so-min-pin-length; flags = $so-pin-flags; } PIN so-puk { attempts = $so-puk-attempts; min-length = $so-min-puk-length; } filesystem { DF MF { path = 3F00; type = DF; # This is the DIR file EF DIR { type = EF; file-id = 2F00; size = 128; acl = *=NONE; } # 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 = *=NONE; size = 5000; 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; } } } } OpenSC-0.26.1/src/pkcs15init/profile.c000066400000000000000000001751701474147347300173450ustar00rootroot00000000000000/* * Initialize Cards according to PKCS#15 * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Random notes * - the "key" command should go away, it's obsolete */ #include "config.h" #include #include #include #include #include #ifdef HAVE_STRINGS_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include #include #ifdef _WIN32 #include #include #endif #include "common/compat_strlcpy.h" #include "scconf/scconf.h" #include "libopensc/log.h" #include "libopensc/pkcs15.h" #include "pkcs15-init.h" #include "profile.h" #define DEF_PRKEY_RSA_ACCESS 0x1D #define DEF_PUBKEY_ACCESS 0x12 #define TEMPLATE_FILEID_MIN_DIFF 0x20 #define WORD_SIZE 64 /* #define DEBUG_PROFILE */ /* * Parser state */ struct state { struct state * frame; const char * filename; struct sc_profile * profile; struct file_info * file; struct pin_info * pin; struct auth_info * key; }; struct command { const char * name; int min_args, max_args; int (*func)(struct state *, int, char **); }; struct block { const char * name; int (*handler)(struct state *, struct block *, const char *, scconf_block *); struct command * cmd_info; struct block * blk_info; }; struct map { const char * name; unsigned int val; }; static struct map aclNames[] = { { "NONE", SC_AC_NONE }, { "NEVER", SC_AC_NEVER }, { "CHV", SC_AC_CHV }, { "TERM", SC_AC_TERM }, { "PRO", SC_AC_PRO }, { "AUT", SC_AC_AUT }, { "KEY", SC_AC_AUT }, { "SEN", SC_AC_SEN }, { "IDA", SC_AC_IDA }, { "SCB", SC_AC_SCB }, { NULL, 0 } }; static struct map fileOpNames[] = { { "SELECT", SC_AC_OP_SELECT }, { "LOCK", SC_AC_OP_LOCK }, { "DELETE", SC_AC_OP_DELETE }, { "DELETE-SELF",SC_AC_OP_DELETE_SELF }, { "CREATE", SC_AC_OP_CREATE }, { "CREATE-EF", SC_AC_OP_CREATE_EF }, { "CREATE-DF", SC_AC_OP_CREATE_DF }, { "REHABILITATE",SC_AC_OP_REHABILITATE }, { "INVALIDATE", SC_AC_OP_INVALIDATE }, { "FILES", SC_AC_OP_LIST_FILES }, { "READ", SC_AC_OP_READ }, { "UPDATE", SC_AC_OP_UPDATE }, { "WRITE", SC_AC_OP_WRITE }, { "ERASE", SC_AC_OP_ERASE }, { "CRYPTO", SC_AC_OP_CRYPTO }, { "PIN-DEFINE", SC_AC_OP_PIN_DEFINE }, { "PIN-CHANGE", SC_AC_OP_PIN_CHANGE }, { "PIN-RESET", SC_AC_OP_PIN_RESET }, { "PIN-USE", SC_AC_OP_PIN_USE }, { "GENERATE", SC_AC_OP_GENERATE }, { "PSO-COMPUTE-SIGNATURE", SC_AC_OP_PSO_COMPUTE_SIGNATURE }, { "INTERNAL-AUTHENTICATE", SC_AC_OP_INTERNAL_AUTHENTICATE }, { "PSO-DECRYPT", SC_AC_OP_PSO_DECRYPT }, { "RESIZE", SC_AC_OP_RESIZE }, { "ADMIN", SC_AC_OP_ADMIN }, { "ACTIVATE", SC_AC_OP_ACTIVATE }, { "DEACTIVATE", SC_AC_OP_DEACTIVATE }, { NULL, 0 } }; static struct map fileTypeNames[] = { { "EF", SC_FILE_TYPE_WORKING_EF }, { "INTERNAL-EF",SC_FILE_TYPE_INTERNAL_EF }, { "DF", SC_FILE_TYPE_DF }, { "BSO", SC_FILE_TYPE_BSO }, { NULL, 0 } }; static struct map fileStructureNames[] = { { "TRANSPARENT", SC_FILE_EF_TRANSPARENT }, { "LINEAR-FIXED", SC_FILE_EF_LINEAR_FIXED }, { "LINEAR-FIXED-TLV", SC_FILE_EF_LINEAR_FIXED_TLV }, { "LINEAR-VARIABLE", SC_FILE_EF_LINEAR_VARIABLE }, { "LINEAR-VARIABLE-TLV",SC_FILE_EF_LINEAR_VARIABLE_TLV }, { "CYCLIC", SC_FILE_EF_CYCLIC }, { "CYCLIC-TLV", SC_FILE_EF_CYCLIC_TLV }, { NULL, 0 } }; static struct map pkcs15DfNames[] = { { "PRKDF", SC_PKCS15_PRKDF }, { "PUKDF", SC_PKCS15_PUKDF }, { "PUKDF-TRUSTED", SC_PKCS15_PUKDF_TRUSTED }, { "SKDF", SC_PKCS15_SKDF }, { "CDF", SC_PKCS15_CDF }, { "CDF-TRUSTED", SC_PKCS15_CDF_TRUSTED }, { "CDF-USEFUL", SC_PKCS15_CDF_USEFUL }, { "DODF", SC_PKCS15_DODF }, { "AODF", SC_PKCS15_AODF }, { NULL, 0 } }; static struct map pinTypeNames[] = { { "BCD", SC_PKCS15_PIN_TYPE_BCD }, { "ascii-numeric", SC_PKCS15_PIN_TYPE_ASCII_NUMERIC }, { "utf8", SC_PKCS15_PIN_TYPE_UTF8 }, { "half-nibble-bcd", SC_PKCS15_PIN_TYPE_HALFNIBBLE_BCD }, { "iso9564-1", SC_PKCS15_PIN_TYPE_ISO9564_1 }, { NULL, 0 } }; static struct map pinIdNames[] = { { "pin", SC_PKCS15INIT_USER_PIN }, { "puk", SC_PKCS15INIT_USER_PUK }, { "user-pin", SC_PKCS15INIT_USER_PIN }, { "user-puk", SC_PKCS15INIT_USER_PUK }, { "sopin", SC_PKCS15INIT_SO_PIN }, { "sopuk", SC_PKCS15INIT_SO_PUK }, { "so-pin", SC_PKCS15INIT_SO_PIN }, { "so-puk", SC_PKCS15INIT_SO_PUK }, { NULL, 0 } }; static struct map pinFlagNames[] = { { "case-sensitive", SC_PKCS15_PIN_FLAG_CASE_SENSITIVE }, { "local", SC_PKCS15_PIN_FLAG_LOCAL }, { "change-disabled", SC_PKCS15_PIN_FLAG_CHANGE_DISABLED }, { "unblock-disabled", SC_PKCS15_PIN_FLAG_UNBLOCK_DISABLED }, { "initialized", SC_PKCS15_PIN_FLAG_INITIALIZED }, { "needs-padding", SC_PKCS15_PIN_FLAG_NEEDS_PADDING }, { "unblockingPin", SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN }, { "soPin", SC_PKCS15_PIN_FLAG_SO_PIN }, { "disable-allowed", SC_PKCS15_PIN_FLAG_DISABLE_ALLOW }, { "integrity-protected", SC_PKCS15_PIN_FLAG_INTEGRITY_PROTECTED }, { "confidentiality-protected", SC_PKCS15_PIN_FLAG_CONFIDENTIALITY_PROTECTED }, { "exchangeRefData", SC_PKCS15_PIN_FLAG_EXCHANGE_REF_DATA }, { NULL, 0 } }; static struct map idStyleNames[] = { { "native", SC_PKCS15INIT_ID_STYLE_NATIVE }, { "mozilla", SC_PKCS15INIT_ID_STYLE_MOZILLA }, { "rfc2459", SC_PKCS15INIT_ID_STYLE_RFC2459 }, { NULL, 0 } }; static struct map mdStyleNames[] = { { "none", SC_PKCS15INIT_MD_STYLE_NONE }, { "gemalto", SC_PKCS15INIT_MD_STYLE_GEMALTO }, { NULL, 0 } }; static struct { const char * name; struct map * addr; } mapNames[] = { { "file ACL", aclNames }, { "file operation", fileOpNames }, { "file type", fileTypeNames }, { "file structure", fileStructureNames}, { "PKCS#15 file name", pkcs15DfNames }, { "pin encoding", pinTypeNames }, { "pin name", pinIdNames }, { "pin flag", pinFlagNames }, { NULL, NULL } }; static int process_conf(struct sc_profile *, scconf_context *); static int process_block(struct state *, struct block *, const char *, scconf_block *); static void init_state(struct state *, struct state *); static int get_authid(struct state *, const char *, unsigned int *, unsigned int *); static int get_uint(struct state *, const char *, unsigned int *); static int get_bool(struct state *, const char *, unsigned int *); static int get_uint_eval(struct state *, int, char **, unsigned int *); static int map_str2int(struct state *, const char *, unsigned int *, struct map *); static int setstr(char **strp, const char *value); static void parse_error(struct state *, const char *, ...); static struct file_info * sc_profile_instantiate_file(sc_profile_t *, struct file_info *, struct file_info *, unsigned int); static struct file_info * sc_profile_find_file(struct sc_profile *, const sc_path_t *, const char *); static struct file_info * sc_profile_find_file_by_path( struct sc_profile *, const sc_path_t *); static struct pin_info * new_pin(struct sc_profile *, int); static struct file_info * new_file(struct state *, const char *, unsigned int); static struct file_info * add_file(sc_profile_t *, const char *, sc_file_t *, struct file_info *); static void free_file_list(struct file_info **); static void append_file(sc_profile_t *, struct file_info *); 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 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) { struct sc_file *file; unsigned int op; file = sc_file_new(); for (op = 0; op < SC_MAX_AC_OPS; op++) { sc_file_add_acl_entry(file, op, SC_AC_NONE, 0); } file->type = type; file->status = SC_FILE_STATUS_ACTIVATED; if (file->type != SC_FILE_TYPE_DF && file->type != SC_FILE_TYPE_BSO) file->ef_structure = SC_FILE_EF_TRANSPARENT; return file; } /* * Initialize profile */ struct sc_profile * sc_profile_new(void) { struct sc_pkcs15_card *p15card; struct sc_profile *pro; pro = calloc(1, sizeof(*pro)); if (pro == NULL) return NULL; pro->p15_spec = p15card = sc_pkcs15_card_new(); pro->pkcs15.do_last_update = 1; if (p15card) { p15card->tokeninfo->label = strdup("OpenSC Card"); p15card->tokeninfo->manufacturer_id = strdup("OpenSC Project"); p15card->tokeninfo->serial_number = strdup("0000"); p15card->tokeninfo->flags = SC_PKCS15_TOKEN_EID_COMPLIANT; p15card->tokeninfo->version = 0; /* Set up EF(TokenInfo) and EF(ODF) */ p15card->file_tokeninfo = init_file(SC_FILE_TYPE_WORKING_EF); p15card->file_odf = init_file(SC_FILE_TYPE_WORKING_EF); p15card->file_unusedspace = init_file(SC_FILE_TYPE_WORKING_EF); } /* Assume card does RSA natively */ pro->rsa_access_flags = DEF_PRKEY_RSA_ACCESS; pro->pin_encoding = SC_PKCS15_PIN_TYPE_ASCII_NUMERIC; pro->pin_minlen = 4; pro->pin_maxlen = 8; pro->id_style = SC_PKCS15INIT_ID_STYLE_NATIVE; return pro; } int sc_profile_load(struct sc_profile *profile, const char *filename) { struct sc_context *ctx = profile->card->ctx; scconf_context *conf; const char *profile_dir = NULL; char path[PATH_MAX]; int res = 0, i; #ifdef _WIN32 char temp_path[PATH_MAX]; size_t temp_len; #endif LOG_FUNC_CALLED(ctx); for (i = 0; ctx->conf_blocks[i]; i++) { profile_dir = scconf_get_str(ctx->conf_blocks[i], "profile_dir", NULL); if (profile_dir) break; } if (!profile_dir) { #ifdef _WIN32 temp_len = PATH_MAX - 1; res = sc_ctx_win32_get_config_value(NULL, "ProfileDir", "Software\\OpenSC Project\\OpenSC", 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; #endif } sc_log(ctx, "Using profile directory '%s'.", profile_dir); #ifdef _WIN32 snprintf(path, sizeof(path), "%s\\%s.%s", profile_dir, filename, SC_PKCS15_PROFILE_SUFFIX); #else /* _WIN32 */ snprintf(path, sizeof(path), "%s/%s.%s", profile_dir, filename, SC_PKCS15_PROFILE_SUFFIX); #endif /* _WIN32 */ sc_log(ctx, "Trying profile file %s", path); conf = scconf_new(path); res = scconf_parse(conf); sc_log(ctx, "profile %s loaded ok", path); if (res < 0) { scconf_free(conf); LOG_FUNC_RETURN(ctx, SC_ERROR_FILE_NOT_FOUND); } if (res == 0) { scconf_free(conf); LOG_FUNC_RETURN(ctx, SC_ERROR_SYNTAX_ERROR); } res = process_conf(profile, conf); scconf_free(conf); LOG_FUNC_RETURN(ctx, res); } int sc_profile_finish(struct sc_profile *profile, const struct sc_app_info *app_info) { struct sc_context *ctx = profile->card->ctx; struct file_info *fi; struct pin_info *pi; char reason[64]; LOG_FUNC_CALLED(ctx); profile->mf_info = sc_profile_find_file(profile, NULL, "MF"); if (!profile->mf_info) LOG_TEST_RET(ctx, SC_ERROR_INCONSISTENT_PROFILE, "Profile doesn't define a MF"); if (app_info && app_info->aid.len) { struct sc_path path; sc_log(ctx, "finish profile with '%s' application profile", app_info->label); memset(&path, 0, sizeof(struct sc_path)); path.type = SC_PATH_TYPE_DF_NAME; path.aid = app_info->aid; sc_log(ctx, "Look for file by path '%s'", sc_print_path(&path)); profile->df_info = sc_profile_find_file_by_path(profile, &path); sc_log(ctx, "returned DF info %p", profile->df_info); if (profile->df_info && profile->df_info->profile_extension) { sc_log(ctx, "application profile extension '%s'", profile->df_info->profile_extension); if (sc_profile_load(profile, profile->df_info->profile_extension)) LOG_TEST_RET(ctx, SC_ERROR_INCONSISTENT_PROFILE, "Cannot load application profile extension"); } } profile->df_info = sc_profile_find_file(profile, NULL, "PKCS15-AppDF"); if (!profile->df_info) LOG_TEST_RET(ctx, SC_ERROR_INCONSISTENT_PROFILE, "Profile doesn't define a PKCS15-AppDF"); profile->p15_spec->file_app = profile->df_info->file; profile->df_info->dont_free = 1; for (pi = profile->pin_list; pi; pi = pi->next) { const char *name; set_pin_defaults(profile, pi); if (!(name = pi->file_name)) continue; if (!(fi = sc_profile_find_file(profile, NULL, name))) { snprintf(reason, sizeof(reason), "unknown PIN file \"%s\"\n", name); goto whine; } pi->file = fi; } LOG_FUNC_RETURN(ctx, SC_SUCCESS); whine: sc_log(ctx, "%s", reason); LOG_FUNC_RETURN(ctx, SC_ERROR_INCONSISTENT_PROFILE); } void sc_profile_free(struct sc_profile *profile) { struct auth_info *ai; struct pin_info *pi; sc_macro_t *mi; sc_template_t *ti; if (profile->name) free(profile->name); if (profile->driver) free(profile->driver); free_file_list(&profile->ef_list); while ((ai = profile->auth_list) != NULL) { profile->auth_list = ai->next; free(ai); } while ((ti = profile->template_list) != NULL) { profile->template_list = ti->next; if (ti->data) sc_profile_free(ti->data); if (ti->name) free(ti->name); free(ti); } while ((mi = profile->macro_list) != NULL) { profile->macro_list = mi->next; if (mi->name) free(mi->name); free(mi); } while ((pi = profile->pin_list) != NULL) { profile->pin_list = pi->next; if (pi->file_name) free(pi->file_name); 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); free(profile); } void sc_profile_get_pin_info(struct sc_profile *profile, int id, struct sc_pkcs15_auth_info *info) { struct pin_info *pi; pi = new_pin(profile, id); if (pi == NULL) return; pi->pin.max_tries = pi->pin.tries_left; *info = pi->pin; } int sc_profile_get_pin_retries(sc_profile_t *profile, int id) { struct pin_info *pi; pi = new_pin(profile, id); if (pi == NULL) return SC_ERROR_OUT_OF_MEMORY; return pi->pin.tries_left; } int sc_profile_get_pin_id(struct sc_profile *profile, unsigned int reference, int *id) { struct pin_info *pi; for (pi = profile->pin_list; pi; pi = pi->next) { if (pi->pin.auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) continue; if (pi->pin.attrs.pin.reference == (int)reference) { *id = pi->id; return 0; } } return SC_ERROR_OBJECT_NOT_FOUND; } int sc_profile_get_file_in(sc_profile_t *profile, const sc_path_t *path, const char *name, sc_file_t **ret) { struct file_info *fi; if ((fi = sc_profile_find_file(profile, path, name)) == NULL) return SC_ERROR_FILE_NOT_FOUND; sc_file_dup(ret, fi->file); if (*ret == NULL) return SC_ERROR_OUT_OF_MEMORY; return 0; } int sc_profile_get_file(struct sc_profile *profile, const char *name, sc_file_t **ret) { struct file_info *fi; if ((fi = sc_profile_find_file(profile, NULL, name)) == NULL) return SC_ERROR_FILE_NOT_FOUND; sc_file_dup(ret, fi->file); if (*ret == NULL) return SC_ERROR_OUT_OF_MEMORY; return 0; } int sc_profile_get_file_instance(struct sc_profile *profile, const char *name, int index, sc_file_t **ret) { struct sc_context *ctx = profile->card->ctx; struct file_info *fi; struct sc_file *file; int r; LOG_FUNC_CALLED(ctx); sc_log(ctx, "try to get '%s' file instance", 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 ? 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)); file->id += index; if(file->type == SC_FILE_TYPE_BSO) { r = sc_profile_add_file(profile, name, file); if (r < 0) sc_file_free(file); LOG_TEST_RET(ctx, r, "Profile error: cannot add BSO file"); } else if (file->path.len) { file->path.value[file->path.len - 2] = (file->id >> 8) & 0xFF; file->path.value[file->path.len - 1] = file->id & 0xFF; r = sc_profile_add_file(profile, name, file); if (r < 0) sc_file_free(file); LOG_TEST_RET(ctx, r, "Profile error: cannot add file"); } if (ret) *ret = file; else sc_file_free(file); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } int sc_profile_get_path(struct sc_profile *profile, const char *name, sc_path_t *ret) { struct file_info *fi; if ((fi = sc_profile_find_file(profile, NULL, name)) == NULL) return SC_ERROR_FILE_NOT_FOUND; *ret = fi->file->path; return 0; } int sc_profile_get_file_by_path(struct sc_profile *profile, const sc_path_t *path, sc_file_t **ret) { struct sc_context *ctx = profile->card->ctx; struct file_info *fi; LOG_FUNC_CALLED(ctx); if ((fi = sc_profile_find_file_by_path(profile, path)) == NULL) LOG_FUNC_RETURN(ctx, SC_ERROR_FILE_NOT_FOUND); sc_file_dup(ret, fi->file); LOG_FUNC_RETURN(ctx, *ret ? SC_SUCCESS : SC_ERROR_OUT_OF_MEMORY); } int sc_profile_add_file(sc_profile_t *profile, const char *name, sc_file_t *file) { struct sc_context *ctx = profile->card->ctx; sc_path_t path = file->path; struct file_info *parent; LOG_FUNC_CALLED(ctx); if (!path.len) { parent = profile->df_info; } else { path.len -= 2; parent = sc_profile_find_file_by_path(profile, &path); } if (!parent) LOG_FUNC_RETURN(ctx, SC_ERROR_FILE_NOT_FOUND); sc_log(ctx, "Parent path:%s", sc_print_path(&parent->file->path)); sc_file_dup(&file, file); if (file == NULL) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); add_file(profile, name, file, parent); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } /* * Instantiate template */ int sc_profile_instantiate_template(sc_profile_t *profile, const char *template_name, const sc_path_t *base_path, const char *file_name, const sc_pkcs15_id_t *id, sc_file_t **ret) { struct sc_context *ctx = profile->card->ctx; struct sc_profile *tmpl; struct sc_template *info; unsigned int idx; struct file_info *fi, *base_file, *match = NULL; #ifdef DEBUG_PROFILE printf("Instantiate %s in template %s\n", file_name, template_name); sc_profile_find_file_by_path(profile, base_path); #endif for (info = profile->template_list; info; info = info->next) if (!strcmp(info->name, template_name)) break; if (info == NULL) { sc_log(ctx, "Template %s not found", template_name); return SC_ERROR_TEMPLATE_NOT_FOUND; } tmpl = info->data; idx = id->value[id->len-1]; for (fi = profile->ef_list; fi; fi = fi->next) { if (fi->base_template == tmpl && fi->inst_index == idx && sc_compare_path(&fi->inst_path, base_path) && !strcmp(fi->ident, file_name)) { sc_file_dup(ret, fi->file); if (*ret == NULL) return SC_ERROR_OUT_OF_MEMORY; return 0; } } sc_log(ctx, "Instantiating template %s at %s", template_name, sc_print_path(base_path)); base_file = sc_profile_find_file_by_path(profile, base_path); if (base_file == NULL) { sc_log(ctx, "Directory %s not defined in profile", sc_print_path(base_path)); return SC_ERROR_OBJECT_NOT_FOUND; } /* 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); for (fi = tmpl->ef_list; fi; fi = fi->next) { struct file_info *parent, *instance; unsigned int skew = 0; fi->instance = NULL; if ((parent = fi->parent) == NULL) { parent = base_file; skew = idx; } parent = parent->instance; instance = sc_profile_instantiate_file(profile, fi, parent, skew); if (instance == NULL) return SC_ERROR_OUT_OF_MEMORY; instance->base_template = tmpl; instance->inst_index = idx; instance->inst_path = *base_path; if (!strcmp(instance->ident, file_name)) match = instance; } if (match == NULL) { sc_log(ctx, "No file named \"%s\" in template \"%s\"", file_name, template_name); return SC_ERROR_OBJECT_NOT_FOUND; } sc_file_dup(ret, match->file); if (*ret == NULL) return SC_ERROR_OUT_OF_MEMORY; #ifdef DEBUG_PROFILE printf("Template instantiated\n"); #endif return 0; } static struct file_info * sc_profile_instantiate_file(sc_profile_t *profile, struct file_info *ft, struct file_info *parent, unsigned int skew) { struct sc_context *ctx = profile->card->ctx; struct file_info *fi; fi = calloc(1, sizeof(*fi)); if (fi == NULL) return NULL; fi->instance = fi; fi->parent = parent; fi->ident = strdup(ft->ident); if (fi->ident == NULL) { free(fi); return NULL; } sc_file_dup(&fi->file, ft->file); if (fi->file == NULL) { free(fi->ident); free(fi); return NULL; } fi->file->path = parent->file->path; fi->file->id += skew; if (fi->file->type == SC_FILE_TYPE_INTERNAL_EF || fi->file->type == SC_FILE_TYPE_WORKING_EF || (fi->file->type == SC_FILE_TYPE_DF && fi->file->id)) sc_append_file_id(&fi->file->path, fi->file->id); append_file(profile, fi); ft->instance = fi; sc_log(ctx, "Instantiated %s at %s", ft->ident, sc_print_path(&fi->file->path)); sc_log(ctx, " parent=%s@%s", parent->ident, sc_print_path(&parent->file->path)); return fi; } int sc_profile_get_pin_id_by_reference(struct sc_profile *profile, unsigned auth_method, int reference, struct sc_pkcs15_auth_info *auth_info) { struct pin_info *pinfo; for (pinfo = profile->pin_list; pinfo; pinfo = pinfo->next) { if (auth_method == SC_AC_SYMBOLIC) { if (pinfo->id != reference) continue; } else { if (pinfo->pin.auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) continue; if (pinfo->pin.auth_method != auth_method) continue; if (pinfo->pin.attrs.pin.reference != reference) continue; } if (auth_info) *auth_info = pinfo->pin; return pinfo->id; } return -1; } /* * Configuration file parser */ static void init_state(struct state *cur_state, struct state *new_state) { memset(new_state, 0, sizeof(*new_state)); new_state->filename = cur_state->filename; new_state->profile = cur_state->profile; new_state->frame = cur_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; } static int do_maxpinlength(struct state *cur, int argc, char **argv) { return get_uint(cur, argv[0], &cur->profile->pin_maxlen); } static int do_minpinlength(struct state *cur, int argc, char **argv) { return get_uint(cur, argv[0], &cur->profile->pin_minlen); } static int do_default_pin_type(struct state *cur, int argc, char **argv) { return map_str2int(cur, argv[0], &cur->profile->pin_encoding, pinTypeNames); } static int do_pin_pad_char(struct state *cur, int argc, char **argv) { return get_uint(cur, argv[0], &cur->profile->pin_pad_char); } static int do_pin_domains(struct state *cur, int argc, char **argv) { return get_bool(cur, argv[0], &cur->profile->pin_domains); } static int do_card_label(struct state *cur, int argc, char **argv) { struct sc_pkcs15_card *p15card = cur->profile->p15_spec; return setstr(&p15card->tokeninfo->label, argv[0]); } static int do_card_manufacturer(struct state *cur, int argc, char **argv) { struct sc_pkcs15_card *p15card = cur->profile->p15_spec; return setstr(&p15card->tokeninfo->manufacturer_id, argv[0]); } /* * Command related to the pkcs15 we generate */ static int do_direct_certificates(struct state *cur, int argc, char **argv) { return get_bool(cur, argv[0], &cur->profile->pkcs15.direct_certificates); } static int do_encode_df_length(struct state *cur, int argc, char **argv) { return get_bool(cur, argv[0], &cur->profile->pkcs15.encode_df_length); } static int do_encode_update_field(struct state *cur, int argc, char **argv) { return get_bool(cur, argv[0], &cur->profile->pkcs15.do_last_update); } static int do_pkcs15_id_style(struct state *cur, int argc, char **argv) { return map_str2int(cur, argv[0], &cur->profile->id_style, idStyleNames); } static int do_minidriver_support_style(struct state *cur, int argc, char **argv) { return map_str2int(cur, argv[0], &cur->profile->md_style, mdStyleNames); } /* * Process an option block */ static int process_option(struct state *cur, struct block *info, const char *name, scconf_block *blk) { sc_profile_t *profile = cur->profile; int match = 0, i; for (i = 0; profile->options[i]; i++) match |= !strcmp(profile->options[i], name); if (!match && strcmp("default", name)) return 0; return process_block(cur, info, name, blk); } /* * Process a key block */ static int process_key(struct state *cur, struct block *info, const char *name, scconf_block *blk) { unsigned int type, id; struct state state; if (get_authid(cur, name, &type, &id)) return 1; init_state(cur, &state); state.key = new_key(cur->profile, type, id); return process_block(&state, info, name, blk); } static struct auth_info * new_key(struct sc_profile *profile, unsigned int type, unsigned int ref) { struct auth_info *ai, **aip; for (aip = &profile->auth_list; (ai = *aip); aip = &ai->next) { if (ai->type == type && ai->ref == ref) return ai; } ai = calloc(1, sizeof(*ai)); if (ai == NULL) return NULL; ai->type = type; ai->ref = ref; *aip = ai; return ai; } static int do_key_value(struct state *cur, int argc, char **argv) { struct auth_info *ai = cur->key; const char *key = argv[0]; size_t key_len; unsigned char keybuf[32]; if (key[0] == '=') { ++key; key_len = strlen(key); memcpy(keybuf, key, key_len); } else { key_len = sizeof(keybuf); if (sc_hex_to_bin(key, keybuf, &key_len)) { parse_error(cur, "Error parsing PIN/key \"%s\"\n", key); return 1; } } memcpy(ai->key, keybuf, key_len); ai->key_len = key_len; return 0; } /* * This function is called when the parser finds a block with an unknown * name in the filesystem block. This will create a new filesystem * object as the child of the current object. */ static int process_df(struct state *cur, struct block *info, const char *name, scconf_block *blk) { struct state state; init_state(cur, &state); if (name == NULL) { parse_error(cur, "No name given for DF object."); return 1; } if (!(state.file = new_file(cur, name, SC_FILE_TYPE_DF))) return 1; return process_block(&state, info, name, blk); } static int process_ef(struct state *cur, struct block *info, const char *name, scconf_block *blk) { struct state state; init_state(cur, &state); if (name == NULL) { parse_error(cur, "No name given for EF object."); return 1; } if (!(state.file = new_file(cur, name, SC_FILE_TYPE_WORKING_EF))) return 1; return process_block(&state, info, name, blk); } static int process_bso(struct state *cur, struct block *info, const char *name, scconf_block *blk) { struct state state; init_state(cur, &state); if (name == NULL) { parse_error(cur, "No name given for BSO object."); return 1; } if (!(state.file = new_file(cur, name, SC_FILE_TYPE_BSO))) return 1; return process_block(&state, info, name, blk); } /* * In the template the difference between any two file-ids * should be greater then TEMPLATE_FILEID_MIN_DIFF. */ static int template_sanity_check(struct state *cur, struct sc_profile *templ) { struct file_info *fi, *ffi; for (fi = templ->ef_list; fi; fi = fi->next) { struct sc_path fi_path = fi->file->path; 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; 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) continue; parse_error(cur, "Template insane: file-ids should be substantially different"); return 1; } } } return SC_SUCCESS; } static int process_tmpl(struct state *cur, struct block *info, const char *name, scconf_block *blk) { struct state state; sc_template_t *tinfo; sc_profile_t *templ; int r; #ifdef DEBUG_PROFILE printf("Process template:%s; block:%s\n", name, info->name); #endif if (name == NULL) { parse_error(cur, "No name given for template."); return 1; } templ = calloc(1, sizeof(*templ)); if (templ == NULL) { parse_error(cur, "memory allocation failed"); return 1; } tinfo = calloc(1, sizeof(*tinfo)); if (tinfo == NULL) { parse_error(cur, "memory allocation failed"); free(templ); return 1; } tinfo->name = strdup(name); tinfo->data = templ; tinfo->next = cur->profile->template_list; cur->profile->template_list = tinfo; init_state(cur, &state); state.profile = tinfo->data; state.file = NULL; r = process_block(&state, info, name, blk); if (!r) r = template_sanity_check(cur, templ); #ifdef DEBUG_PROFILE printf("Template %s processed; returns %i\n", name, r); #endif return r; } /* * Append new file at the end of the ef_list. * This is crucial; the profile instantiation code relies on it */ static void append_file(sc_profile_t *profile, struct file_info *nfile) { struct file_info **list, *fi; list = &profile->ef_list; while ((fi = *list) != NULL) list = &fi->next; *list = nfile; } /* * Add a new file to the profile. * This function is called by sc_profile_add_file. */ static struct file_info * add_file(sc_profile_t *profile, const char *name, sc_file_t *file, struct file_info *parent) { struct file_info *info; info = calloc(1, sizeof(*info)); if (info == NULL) return NULL; info->instance = info; info->ident = strdup(name); info->parent = parent; info->file = file; append_file(profile, info); return info; } /* * Free file_info list */ static void free_file_list(struct file_info **list) { struct file_info *fi; while ((fi = *list) != NULL) { *list = fi->next; if (fi->dont_free == 0) sc_file_free(fi->file); free(fi->profile_extension); free(fi->ident); free(fi); } } /* * Create a new file info object. * This function is called by the profile parser. */ static struct file_info * new_file(struct state *cur, const char *name, unsigned int type) { sc_profile_t *profile = cur->profile; 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; /* Special cases for those EFs handled separately * 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) || 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->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 (free_file) sc_file_free(file); return NULL; } info = add_file(profile, name, file, cur->file); if (info == NULL) { parse_error(cur, "memory allocation failed"); return NULL; } info->dont_free = dont_free; return info; } static int 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; return 0; } static int do_file_path(struct state *cur, int argc, char **argv) { 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 */ sc_format_path(argv[0], path); if (!path->len || (path->len & 1)) { parse_error(cur, "Invalid path length\n"); return 1; } file->id = (path->value[path->len-2] << 8) | path->value[path->len-1]; return 0; } static int do_fileid(struct state *cur, int argc, char **argv) { struct file_info *fi; 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 */ sc_format_path(argv[0], &temp); if (temp.len != 2) { parse_error(cur, "Invalid file ID length\n"); return 1; } /* Get the DF, if any */ if ((fi = cur->file->parent) && (df = fi->file)) { if (!df->path.len && !df->path.aid.len) { parse_error(cur, "No path/fileid set for parent DF\n"); return 1; } if (df->path.len + 2 > sizeof(df->path.value)) { parse_error(cur, "File path too long\n"); return 1; } *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; file->id = (temp.value[0] << 8) | temp.value[1]; return 0; } static int 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; return 0; } static int 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; return 0; } static int 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; return 0; } static int do_content(struct state *cur, int argc, char **argv) { 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; rv = sc_hex_to_bin(argv[0], file->encoded_content, &len); file->encoded_content_len = len; return rv; } static int do_prop_attr(struct state *cur, int argc, char **argv) { 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; rv = sc_hex_to_bin(argv[0], file->prop_attr, &len); file->prop_attr_len = len; return rv; } static int do_aid(struct state *cur, int argc, char **argv) { struct sc_file *file = NULL; const char *name = argv[0]; 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)) { parse_error(cur, "AID \"%s\" too long\n", name); return 1; } memcpy(file->name, name, len); file->namelen = len; } else { file->namelen = sizeof(file->name); res = sc_hex_to_bin(name, file->name, &file->namelen); } return res; } static int do_exclusive_aid(struct state *cur, int argc, char **argv) { struct sc_file *file = NULL; const char *name = argv[0]; 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)); #endif sc_format_path(name, &file->path); if (file->path.len > SC_MAX_AID_SIZE) { parse_error(cur, "Path length is too big\n"); return 1; } memcpy(file->path.aid.value, file->path.value, file->path.len); file->path.aid.len = file->path.len; file->path.len = 0; file->path.type = SC_PATH_TYPE_DF_NAME; #ifdef DEBUG_PROFILE printf("do_exclusive_aid(): '%s' exclusive-aid path %s\n", cur->file->ident, sc_print_path(&file->path)); #endif if (*name == '=') { len = strlen(++name); if (len > sizeof(file->name)) { parse_error(cur, "AID \"%s\" too long\n", name); return 1; } memcpy(file->name, name, len); file->namelen = len; } else { file->namelen = sizeof(file->name); res = sc_hex_to_bin(name, file->name, &file->namelen); } return res; } 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]); } /* * Parse ACL list. * The way we do this is we first split things like CHV1 * into a method (SC_AC_CHV) and a reference (1). * When we're finished parsing the profile, the fake references * are replaced by the real references given in KEY or PIN * commands */ static int do_acl(struct state *cur, int argc, char **argv) { 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'; if (*what == '$') { method = SC_AC_SYMBOLIC; if (map_str2int(cur, what+1, &id, pinIdNames)) return 1; } else if (get_authid(cur, what, &method, &id)) goto bad; if (!strcmp(oper, "*")) { for (op = 0; op < SC_MAX_AC_OPS; op++) { sc_file_clear_acl_entries(file, op); sc_file_add_acl_entry(file, op, method, id); } } else { const sc_acl_entry_t *acl; if (map_str2int(cur, oper, &op, fileOpNames)) goto bad; 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) sc_file_clear_acl_entries(file, op); sc_file_add_acl_entry(file, op, method, id); } } return 0; bad: parse_error(cur, "Invalid ACL \"%s%s%s\"\n", oper, what? "=" : "", what? what : ""); return 1; } static int process_pin(struct state *cur, struct block *info, const char *name, scconf_block *blk) { struct state state; unsigned int id; if (map_str2int(cur, name, &id, pinIdNames)) return 1; init_state(cur, &state); state.pin = new_pin(cur->profile, (int)id); return process_block(&state, info, name, blk); } static struct pin_info * new_pin(struct sc_profile *profile, int id) { struct pin_info *pi, **tail; for (tail = &profile->pin_list; (pi = *tail); tail = &pi->next) { if (pi->id == id) return pi; } /* Create pin info object. Most values are * set to their defaults in set_pin_defaults later * We can't do this here because these pin info objects * are usually created before we've read the card specific * profile */ pi = calloc(1, sizeof(*pi)); if (pi == NULL) return NULL; pi->id = id; pi->pin.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; pi->pin.auth_method = SC_AC_CHV; pi->pin.attrs.pin.type = (unsigned int)-1; pi->pin.attrs.pin.flags = 0x32; pi->pin.attrs.pin.max_length = 0; pi->pin.attrs.pin.min_length = 0; pi->pin.attrs.pin.stored_length = 0; pi->pin.attrs.pin.pad_char = 0xA5; pi->pin.attrs.pin.reference = -1; pi->pin.tries_left = 3; *tail = pi; return pi; } static void set_pin_defaults(struct sc_profile *profile, struct pin_info *pi) { struct sc_pkcs15_auth_info *info = &pi->pin; struct sc_pkcs15_pin_attributes *pin_attrs = &info->attrs.pin; info->auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; if (pin_attrs->type == (unsigned int) -1) pin_attrs->type = profile->pin_encoding; if (pin_attrs->max_length == 0) pin_attrs->max_length = profile->pin_maxlen; if (pin_attrs->min_length == 0) pin_attrs->min_length = profile->pin_minlen; if (pin_attrs->stored_length == 0) { pin_attrs->stored_length = profile->pin_maxlen; /* BCD encoded PIN takes half the space */ if (pin_attrs->type == SC_PKCS15_PIN_TYPE_BCD) pin_attrs->stored_length = (pin_attrs->stored_length + 1) / 2; } if (pin_attrs->pad_char == 0xA5) pin_attrs->pad_char = profile->pin_pad_char; } 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; } static int do_pin_offset(struct state *cur, int argc, char **argv) { return get_uint(cur, argv[0], &cur->pin->file_offset); } static int do_pin_attempts(struct state *cur, int argc, char **argv) { struct pin_info *pi = cur->pin; unsigned int count; if (get_uint(cur, argv[0], &count)) return 1; pi->pin.tries_left = count; return 0; } static int do_pin_maxunlocks(struct state *cur, int argc, char **argv) { struct pin_info *pi = cur->pin; unsigned int count; if (get_uint(cur, argv[0], &count)) return 1; pi->pin.max_unlocks = count; return 0; } static int do_pin_type(struct state *cur, int argc, char **argv) { unsigned int type; if (map_str2int(cur, argv[0], &type, pinTypeNames)) return 1; if (cur->pin->pin.auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return 1; cur->pin->pin.attrs.pin.type = type; return 0; } static int do_pin_reference(struct state *cur, int argc, char **argv) { unsigned int reference; if (get_uint(cur, argv[0], &reference)) return 1; if (cur->pin->pin.auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return 1; cur->pin->pin.attrs.pin.reference = reference; return 0; } static int do_pin_authid(struct state *cur, int argc, char **argv) { sc_pkcs15_format_id(argv[0], &cur->pin->pin.auth_id); return 0; } static int do_pin_minlength(struct state *cur, int argc, char **argv) { unsigned int len; if (get_uint(cur, argv[0], &len)) return 1; if (cur->pin->pin.auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return 1; cur->pin->pin.attrs.pin.min_length = len; return 0; } static int do_pin_maxlength(struct state *cur, int argc, char **argv) { unsigned int len; if (get_uint(cur, argv[0], &len)) return 1; if (cur->pin->pin.auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return 1; cur->pin->pin.attrs.pin.max_length = len; return 0; } static int do_pin_storedlength(struct state *cur, int argc, char **argv) { unsigned int len; if (get_uint(cur, argv[0], &len)) return 1; if (cur->pin->pin.auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return 1; cur->pin->pin.attrs.pin.stored_length = len; return 0; } static int do_pin_flags(struct state *cur, int argc, char **argv) { unsigned int flags = 0; int i, r; if (cur->pin->pin.auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return -1; cur->pin->pin.attrs.pin.flags = 0; for (i = 0; i < argc; i++) { if ((r = map_str2int(cur, argv[i], &flags, pinFlagNames)) < 0) return r; cur->pin->pin.attrs.pin.flags |= flags; } return 0; } static int process_macros(struct state *cur, struct block *info, const char *dummy, scconf_block *blk) { 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 || !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 r = new_macro(cur->profile, name, item->value.list); if (r != SC_SUCCESS) return r; } return SC_SUCCESS; } 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 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 * find_macro(sc_profile_t *profile, const char *name) { sc_macro_t *mac; for (mac = profile->macro_list; mac; mac = mac->next) { if (!strcmp(mac->name, name)) return mac; } return NULL; } /* * Key section */ static struct command key_commands[] = { { "value", 1, 1, do_key_value }, { NULL, 0, 0, NULL } }; /* * Cardinfo section */ static struct command ci_commands[] = { { "driver", 1, 1, do_card_driver }, { "max-pin-length", 1, 1, do_maxpinlength }, { "min-pin-length", 1, 1, do_minpinlength }, { "pin-encoding", 1, 1, do_default_pin_type }, { "pin-pad-char", 1, 1, do_pin_pad_char }, { "pin-domains", 1, 1, do_pin_domains }, { "label", 1, 1, do_card_label }, { "manufacturer", 1, 1, do_card_manufacturer}, { NULL, 0, 0, NULL } }; static struct block ci_blocks[] = { { "key", process_key, key_commands, NULL }, { NULL, NULL, NULL, NULL } }; /* * Filesystem section */ static struct command fs_commands[] = { { "type", 1, 1, do_file_type }, { "path", 1, 1, do_file_path }, { "file-id", 1, 1, do_fileid }, { "structure", 1, 1, do_structure }, { "size", 1, -1, do_size }, { "record-length", 1, 1, do_reclength }, { "AID", 1, 1, do_aid }, { "ACL", 1, -1, do_acl }, /* AID dependent sub-profile */ { "profile-extension", 1, 1, do_profile_extension }, /* AID of the DFs without file-id */ { "exclusive-aid", 1, 1, do_exclusive_aid }, { "content", 1, 1, do_content }, { "prop-attr", 1, 1, do_prop_attr }, { NULL, 0, 0, NULL } }; static struct block fs_blocks[] = { { "DF", process_df, fs_commands, fs_blocks }, { "EF", process_ef, fs_commands, fs_blocks }, { "BSO", process_bso, fs_commands, fs_blocks }, { "template", process_tmpl, fs_commands, fs_blocks }, { NULL, NULL, NULL, NULL } }; /* * Pin section */ static struct command pi_commands[] = { { "file", 1, 1, do_pin_file }, { "offset", 1, 1, do_pin_offset }, { "attempts", 1, 2, do_pin_attempts }, { "encoding", 1, 1, do_pin_type }, { "reference", 1, 1, do_pin_reference }, { "auth-id", 1, 1, do_pin_authid }, { "max-length", 1, 1, do_pin_maxlength }, { "min-length", 1, 1, do_pin_minlength }, { "stored-length", 1, 1, do_pin_storedlength }, { "max-unlocks", 1, 1, do_pin_maxunlocks }, { "flags", 1, -1, do_pin_flags }, { NULL, 0, 0, NULL } }; /* * pkcs15 dialect section */ static struct command p15_commands[] = { { "direct-certificates", 1, 1, do_direct_certificates }, { "encode-df-length", 1, 1, do_encode_df_length }, { "do-last-update", 1, 1, do_encode_update_field }, { "pkcs15-id-style", 1, 1, do_pkcs15_id_style }, { "minidriver-support-style", 1, 1, do_minidriver_support_style }, { NULL, 0, 0, NULL } }; static struct block root_blocks[] = { { "filesystem", process_block, NULL, fs_blocks }, { "cardinfo", process_block, ci_commands, ci_blocks }, { "pin", process_pin, pi_commands, NULL }, { "option", process_option, NULL, root_blocks }, { "macros", process_macros, NULL, NULL }, { "pkcs15", process_block, p15_commands, NULL }, { NULL, NULL, NULL, NULL } }; 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(char *str, char word[WORD_SIZE]) { 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; 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 *macro; int r; for (argc = 0; list; list = list->next) { if (argc >= max) { parse_error(cur, "%s: too many arguments", cmdname); return SC_ERROR_INVALID_ARGUMENTS; } str = list->data; if (str[0] != '$') { /* When str contains macro inside, macro reference loop needs to be checked */ char *macro_name = NULL; if ((macro_name = strchr(str, '$'))) { /* Macro does not to start at the first position */ char word[WORD_SIZE]; get_inner_word(macro_name + 1, word); 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 (!(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:", macro->name); for (list = macro->value; list; list = list->next) printf(" %s", list->data); printf("\n"); } #endif r = build_argv(cur, cmdname, macro->value, argv + argc, max - argc); if (r < 0) return r; argc += r; } return argc; } static int process_command(struct state *cur, struct command *cmd_info, scconf_list *list) { const char *cmd = cmd_info->name; char *argv[32]; int argc, max = 32; if (cmd_info->max_args >= 0 && max > cmd_info->max_args) max = cmd_info->max_args; if ((argc = build_argv(cur, cmd, list, argv, max)) < 0) return argc; if (argc < cmd_info->min_args) { parse_error(cur, "%s: not enough arguments\n", cmd); return 1; } return cmd_info->func(cur, argc, argv); } static struct block * find_block_handler(struct block *bp, const char *name) { if (bp == NULL) return NULL; for (; bp->name; bp++) { if (!strcasecmp(bp->name, name)) return bp; } return NULL; } static struct command * find_cmd_handler(struct command *cp, const char *name) { if (cp == NULL) return NULL; for (; cp->name; cp++) { if (!strcasecmp(cp->name, name)) return cp; } return NULL; } static int process_block(struct state *cur, struct block *info, const char *name, scconf_block *blk) { scconf_item *item; struct command *cp; struct block *bp; const char *cmd, *ident; int res = 0; for (item = blk->items; res == 0 && item; item = item->next) { 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; ident = NULL; if ((nlist = item->value.block->name) != NULL) { if (nlist->next) { parse_error(cur, "Too many name components in block name."); return SC_ERROR_SYNTAX_ERROR; } ident = nlist->data; } #ifdef DEBUG_PROFILE printf("Processing %s %s\n", cmd, ident? ident : ""); #endif if ((bp = find_block_handler(info->blk_info, cmd))) { res = bp->handler(cur, bp, ident, item->value.block); continue; } } else if (item->type == SCCONF_ITEM_TYPE_VALUE) { #ifdef DEBUG_PROFILE printf("Processing %s\n", cmd); #endif if ((cp = find_cmd_handler(info->cmd_info, cmd))) { res = process_command(cur, cp, item->value.list); continue; } } parse_error(cur, "Command \"%s\" not understood in this context.", cmd); return SC_ERROR_SYNTAX_ERROR; } if (res > 0) res = SC_ERROR_SYNTAX_ERROR; return res; } static int process_conf(struct sc_profile *profile, scconf_context *conf) { struct state state; memset(&state, 0, sizeof(state)); state.filename = conf->filename; state.profile = profile; return process_block(&state, &root_ops, "root", conf->root); } static struct file_info * sc_profile_find_file(struct sc_profile *pro, const sc_path_t *path, const char *name) { struct file_info *fi; size_t len; const u8 *value; 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, value, len)) return fi; } return NULL; } static struct file_info * sc_profile_find_file_by_path(struct sc_profile *pro, const sc_path_t *path) { struct file_info *fi, *out = NULL; struct sc_path *fp_path, *fpp_path; #ifdef DEBUG_PROFILE struct sc_context *ctx = pro->card->ctx; sc_log(ctx, "profile's EF list:"); for (fi = pro->ef_list; fi; fi = fi->next) { sc_log(ctx, "'%s' (path:%s)", fi->ident, sc_print_path(&fi->file->path)); sc_log(ctx, "fi parent %p", fi->parent); if (fi->parent && fi->parent->file) sc_log(ctx, "fi parent path %s", sc_print_path(&fi->parent->file->path)); } sc_log(ctx, "find profile file by path:%s", sc_print_path(path)); #endif if (!path || (!path->len && !path->aid.len)) return NULL; for (fi = pro->ef_list; fi; fi = fi->next) { fp_path = &fi->file->path; fpp_path = fi->parent ? &fi->parent->file->path : NULL; if (fp_path->len != path->len) continue; if (fp_path->len && memcmp(fp_path->value, path->value, path->len)) continue; if (path->aid.len && fp_path->aid.len) { if (memcmp(fp_path->aid.value, path->aid.value, path->aid.len)) continue; } else if (path->aid.len && !fp_path->aid.len && fpp_path) { if (fpp_path->type == SC_PATH_TYPE_DF_NAME && fpp_path->len) { if (fpp_path->len != path->aid.len) continue; if (memcmp(fpp_path->value, path->aid.value, path->aid.len)) continue; } else if (fpp_path->aid.len) { if (fpp_path->aid.len != path->aid.len) continue; if (memcmp(fpp_path->aid.value, path->aid.value, path->aid.len)) continue; } } out = fi; } #ifdef DEBUG_PROFILE sc_log(ctx, "returns (%s)", out ? out->ident: ""); #endif return out; } int sc_profile_get_parent(struct sc_profile *profile, const char *name, sc_file_t **ret) { struct file_info *fi = NULL; if ((fi = sc_profile_find_file(profile, NULL, name)) == NULL) return SC_ERROR_FILE_NOT_FOUND; if (!fi->parent) return SC_ERROR_FILE_NOT_FOUND; sc_file_dup(ret, fi->parent->file); if (*ret == NULL) return SC_ERROR_OUT_OF_MEMORY; return 0; } /* * Split up KEY0 or CHV1 into SC_AC_XXX and a number */ static int get_authid(struct state *cur, const char *value, unsigned int *type, unsigned int *num) { char temp[16]; size_t n; 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)); if (map_str2int(cur, temp, type, aclNames)) return 1; if (value[n]) return get_uint(cur, value + n, num); *num = 0; return 0; } static int get_uint(struct state *cur, const char *value, unsigned int *vp) { char *ep; unsigned long tmp; if (strstr(value, "0x") == value) tmp = strtoul(value + 2, &ep, 16); else if (strstr(value, "x") == value) tmp = strtoul(value + 1, &ep, 16); else 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; } static int get_bool(struct state *cur, const char *value, unsigned int *vp) { if (!strcasecmp(value, "on") || !strcasecmp(value, "yes") || !strcasecmp(value, "true")) { *vp = 1; } else if (!strcasecmp(value, "off") || !strcasecmp(value, "no") || !strcasecmp(value, "false")) { *vp = 0; } else { parse_error(cur, "invalid boolean argument \"%s\"\n", value); return 1; } return 0; } static int map_str2int(struct state *cur, const char *value, unsigned int *vp, struct map *map) { unsigned int n; const char *what; if (isdigit((unsigned char) *value)) return get_uint(cur, value, vp); for (n = 0; map[n].name; n++) { if (!strcasecmp(value, map[n].name)) { *vp = map[n].val; return 0; } } /* Try to print a meaningful error message */ what = "argument"; for (n = 0; mapNames[n].name; n++) { if (mapNames[n].addr == map) { what = mapNames[n].name; break; } } parse_error(cur, "invalid %s \"%s\"\n", what, value); return SC_ERROR_SYNTAX_ERROR; } static int setstr(char **strp, const char *value) { if (*strp) free(*strp); *strp = strdup(value); return 0; } /* * Evaluate numeric expressions */ #include struct num_exp_ctx { struct state * state; jmp_buf error; int j; char word[WORD_SIZE]; char * unget; char * str; int argc; char ** argv; }; static void expr_eval(struct num_exp_ctx *, unsigned int *, unsigned int, int); static void expr_fail(struct num_exp_ctx *ctx) { longjmp(ctx->error, 1); } static void expr_put(struct num_exp_ctx *ctx, int c) { if (ctx->j >= (int)sizeof(ctx->word)) expr_fail(ctx); ctx->word[ctx->j++] = (char)c; } static char * __expr_get(struct num_exp_ctx *ctx, int eof_okay) { char *s; if ((s = ctx->unget) != NULL) { ctx->unget = NULL; return s; } ctx->j = 0; s = ctx->str; do { if (s == NULL || *s == '\0') { if (ctx->argc == 0) { if (eof_okay) return NULL; expr_fail(ctx); } ctx->str = s = *(ctx->argv++); ctx->argc--; } while (isspace((unsigned char)*s)) s++; } while (*s == '\0'); if (isdigit((unsigned char)*s)) { while (isdigit((unsigned char)*s)) expr_put(ctx, *s++); } else if (*s == '$') { expr_put(ctx, *s++); while (is_macro_character(*s)) expr_put(ctx, *s++); } else if (strchr("*/+-()|&", *s)) { expr_put(ctx, *s++); } else { expr_fail(ctx); } ctx->str = s; expr_put(ctx, '\0'); return ctx->word; } static char * expr_get(struct num_exp_ctx *ctx) { return __expr_get(ctx, 0); } static void expr_unget(struct num_exp_ctx *ctx, char *s) { if (ctx->unget) expr_fail(ctx); ctx->unget = s; } static void expr_expect(struct num_exp_ctx *ctx, int c) { char *tok; tok = expr_get(ctx); if (tok[0] != (char)c || tok[1]) expr_fail(ctx); } #define MAX_BRACKETS 32 static void expr_term(struct num_exp_ctx *ctx, unsigned int *vp, int opening_brackets) { char *tok; tok = expr_get(ctx); if (*tok == '(') { 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((unsigned char)*tok)) { char *ep; unsigned long tmp; 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; char *argv[32]; int argc; if (!(mac = find_macro(ctx->state->profile, tok + 1))) expr_fail(ctx); argc = build_argv(ctx->state, "", mac->value, argv, 32); if (argc < 0 || get_uint_eval(ctx->state, argc, argv, vp) < 0) expr_fail(ctx); } else { parse_error(ctx->state, "Unexpected token \"%s\" in expression", tok); expr_fail(ctx); } } static void 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, opening_brackets); while (1) { tok = __expr_get(ctx, 1); if (tok == NULL) break; op = tok[0]; new_pri = 0; switch (op) { case '*': case '/': new_pri++; /* fall through */ case '+': case '-': new_pri++; /* fall through */ case '&': new_pri++; /* fall through */ case '|': new_pri++; /* fall through */ case ')': break; default: expr_fail(ctx); } if (new_pri < pri) { expr_unget(ctx, tok); break; } pri = new_pri; expr_eval(ctx, &right, new_pri + 1, opening_brackets); switch (op) { 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; case '|': left |= right; break; default: expr_fail(ctx); } } *vp = left; } static int get_uint_eval(struct state *cur, int argc, char **argv, unsigned int *vp) { struct num_exp_ctx ctx; memset(&ctx, 0, sizeof(ctx)); ctx.state = cur; ctx.argc = argc; ctx.argv = argv; if (setjmp(ctx.error)) { parse_error(cur, "invalid numeric expression\n"); return SC_ERROR_SYNTAX_ERROR; } expr_eval(&ctx, vp, 0, 0); if (ctx.str[0] || ctx.argc) expr_fail(&ctx); return 0; } static void parse_error(struct state *cur, const char *fmt, ...) { char buffer[1024], *sp; va_list ap; va_start(ap, fmt); vsnprintf(buffer, sizeof(buffer), fmt, ap); va_end(ap); if ((sp = strchr(buffer, '\n')) != NULL) *sp = '\0'; if (cur->profile->card && cur->profile->card->ctx) sc_log(cur->profile->card->ctx, "%s: %s", cur->filename, buffer); else fprintf(stdout, "%s: %s\n", cur->filename, buffer); } OpenSC-0.26.1/src/pkcs15init/profile.h000066400000000000000000000117461474147347300173500ustar00rootroot00000000000000/* * Card profile information (internal) * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _OPENSC_PROFILE_H #define _OPENSC_PROFILE_H #ifdef __cplusplus extern "C" { #endif #include "libopensc/pkcs15.h" #ifndef SC_PKCS15_PROFILE_SUFFIX #define SC_PKCS15_PROFILE_SUFFIX "profile" #endif /* Obsolete */ struct auth_info { struct auth_info * next; unsigned int type; /* CHV, AUT, PRO */ unsigned int ref; size_t key_len; u8 key[32]; }; struct file_info { char * ident; struct file_info * next; struct sc_file * file; unsigned int dont_free; struct file_info * parent; /* Template support */ struct file_info * instance; struct sc_profile * base_template; unsigned int inst_index; sc_path_t inst_path; /* Profile extension dependent on the application ID (sub-profile). * Sub-profile is loaded when binding to the particular application * of the multi-application PKCS#15 card. */ char * profile_extension; }; /* For now, we assume the PUK always resides * in the same file as the PIN */ struct pin_info { int id; struct pin_info * next; char * file_name; /* obsolete */ unsigned int file_offset; /* obsolete */ struct file_info * file; /* obsolete */ struct sc_pkcs15_auth_info pin; }; typedef struct sc_macro { char * name; struct sc_macro * next; scconf_list * value; } sc_macro_t; /* Template support. * * Templates are EFs or entire hierarchies of DFs/EFs. * When instantiating a template, the file IDs of the * EFs and DFs are combined from the value given in the * profile, and the last octet of the pkcs15 ID. */ typedef struct sc_template { char * name; struct sc_template * next; struct sc_profile * data; struct file_info * file; } sc_template_t; #define SC_PKCS15INIT_MAX_OPTIONS 16 struct sc_profile { char * name; char * options[SC_PKCS15INIT_MAX_OPTIONS]; sc_card_t * card; char * driver; struct sc_pkcs15init_operations *ops; void * dll; /* handle for dynamic modules */ struct file_info * mf_info; struct file_info * df_info; struct file_info * ef_list; struct sc_file * df[SC_PKCS15_DF_TYPE_COUNT]; struct pin_info * pin_list; struct auth_info * auth_list; sc_template_t * template_list; sc_macro_t * macro_list; unsigned int pin_domains; unsigned int pin_maxlen; unsigned int pin_minlen; unsigned int pin_pad_char; unsigned int pin_encoding; unsigned int pin_attempts; unsigned int puk_attempts; unsigned int rsa_access_flags; struct { unsigned int direct_certificates; unsigned int encode_df_length; unsigned int do_last_update; } pkcs15; /* PKCS15 information */ sc_pkcs15_card_t * p15_spec; /* as given by profile */ sc_pkcs15_card_t * p15_data; /* as found on card */ /* flag to indicate whether the TokenInfo::lastUpdate field * needs to be updated (in other words: if the card content * has been changed) */ int dirty; /* PKCS15 object ID style */ unsigned int id_style; /* Minidriver support style */ unsigned int md_style; }; struct sc_profile *sc_profile_new(void); int sc_profile_load(struct sc_profile *, const char *); int sc_profile_finish(struct sc_profile *, const struct sc_app_info *); void sc_profile_free(struct sc_profile *); int sc_profile_build_pkcs15(struct sc_profile *); void sc_profile_get_pin_info(struct sc_profile *, int, struct sc_pkcs15_auth_info *); int sc_profile_get_pin_id(struct sc_profile *, unsigned int, int *); int sc_profile_get_file(struct sc_profile *, const char *, struct sc_file **); int sc_profile_get_file_by_path(struct sc_profile *, const struct sc_path *, struct sc_file **); int sc_profile_get_path(struct sc_profile *, const char *, struct sc_path *); int sc_profile_get_file_in(struct sc_profile *, const sc_path_t *, const char *, sc_file_t **); int sc_profile_instantiate_template(struct sc_profile *, const char *, const sc_path_t *, const char *, const sc_pkcs15_id_t *, sc_file_t **); int sc_profile_add_file(struct sc_profile *, const char *, sc_file_t *); int sc_profile_get_file_instance(struct sc_profile *, const char *, int, sc_file_t **); int sc_profile_get_pin_id_by_reference(struct sc_profile *, unsigned, int, struct sc_pkcs15_auth_info *); int sc_profile_get_parent(struct sc_profile *profile, const char *, sc_file_t **); #ifdef __cplusplus } #endif #endif /* _OPENSC_PROFILE_H */ OpenSC-0.26.1/src/pkcs15init/rutoken.profile000066400000000000000000000126021474147347300206000ustar00rootroot00000000000000# # PKCS15 profile, generic information. # This profile is loaded before any card specific profile. # cardinfo { label = "Rutoken S"; manufacturer = "Aktiv Co."; max-pin-length = 16; min-pin-length = 1; pin-encoding = ascii-numeric; pin-pad-char = 0xFF; } # # 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; } # 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; } } # This option is for cards with very little memory. # It sets the size of various PKCS15 directory files # to 128 or 256, respectively. option small { macros { ti-size = 64; odf-size = 128; aodf-size = 128; dodf-size = 512; cdf-size = 512; prkdf-size = 512; pukdf-size = 512; } } # 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; min-length = 8; max-length = 16; flags = case-sensitive, initialized; } PIN user-puk { min-length = 0; max-length = 0; } PIN so-pin { auth-id = 1; reference = 1; min-length = 8; max-length = 16; flags = case-sensitive, initialized, soPin; } PIN so-puk { min-length = 0; max-length = 0; } filesystem { DF MF { path = 3F00; type = DF; acl = *=NEVER, SELECT=NONE, DELETE=NEVER, CREATE=CHV2, READ=NONE; EF DIR { type = EF; file-id = 2F00; size = 128; acl = *=NEVER, READ=NONE, UPDATE=CHV2, WRITE=CHV2, DELETE=CHV2; } # 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; size = 0; acl = *=NEVER, SELECT=NONE, DELETE=CHV2, CREATE=CHV2, READ=NONE; EF PKCS15-ODF { file-id = 5031; size = $odf-size; acl = *=NEVER, READ=NONE, UPDATE=CHV2, WRITE=CHV2, DELETE=CHV2; } EF PKCS15-TokenInfo { file-id = 5032; size = $ti-size; acl = *=NEVER, READ=NONE, UPDATE=CHV2, WRITE=CHV2, DELETE=CHV2; } EF PKCS15-AODF { file-id = 4401; size = $aodf-size; acl = *=NEVER, READ=NONE, UPDATE=CHV2, WRITE=CHV2, DELETE=CHV2; } EF PKCS15-PrKDF { file-id = 4402; size = $prkdf-size; acl = *=NEVER, READ=NONE, UPDATE=$PIN, WRITE=$PIN, DELETE=$PIN; } EF PKCS15-PuKDF { file-id = 4403; size = $pukdf-size; acl = *=NEVER, READ=NONE, UPDATE=$PIN, WRITE=$PIN, DELETE=$PIN; } EF PKCS15-CDF { file-id = 4404; size = $cdf-size; acl = *=NEVER, READ=NONE, UPDATE=$PIN, WRITE=$PIN, DELETE=$PIN; } EF PKCS15-DODF { file-id = 4405; 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; } } } } } OpenSC-0.26.1/src/pkcs15init/rutoken_ecp.profile000066400000000000000000000141031474147347300214250ustar00rootroot00000000000000# # PKCS15 profile, generic information. # This profile is loaded before any card specific profile. # cardinfo { label = "Rutoken ECP"; 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 = 6; 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 PuKey-DF { file-id = 6001; } DF PrKey-DF { file-id = 6002; } DF SKey-DF { file-id = 6003; } DF Cer-DF { file-id = 6004; } DF LCHV-DF { file-id = 6005; } } 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; # acl = *=NEVER, SELECT=NONE, DELETE=CHV2, CREATE=CHV2, READ=NONE; 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; } } } } } OpenSC-0.26.1/src/pkcs15init/rutoken_lite.profile000066400000000000000000000131161474147347300216160ustar00rootroot00000000000000# # 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; } } } } } OpenSC-0.26.1/src/pkcs15init/sc-hsm.profile000066400000000000000000000006201474147347300203000ustar00rootroot00000000000000# # PKCS15 r/w profile for SmartCard-HSM cards # cardinfo { label = "SmartCard-HSM"; manufacturer = "CardContact"; max-pin-length = 15; min-pin-length = 6; pin-encoding = ascii-numeric; } filesystem { # Here comes the application DF DF PKCS15-AppDF { type = DF; exclusive-aid = E8:2B:06:01:04:01:81:C3:1F:02:01; acl = *=NONE; } } OpenSC-0.26.1/src/pkcs15init/setcos.profile000066400000000000000000000073171474147347300204200ustar00rootroot00000000000000# # General purpose PKCS15 profile for SetCOS4.4 cards # cardinfo { max-pin-length = 8; pin-encoding = ascii-numeric; pin-pad-char = 0x00; } # Additional default settings option default { macros { protected = *=$SOPIN, READ=NONE; mf_prot = *=NONE, CREATE=$SOPIN; # Allow to delete the MF p15_prot = *=$SOPIN, SELECT=NONE, FILES=NONE, CREATE=NONE; pin_prot = *=NEVER, WRITE=$SOPIN, UPDATE=$SOPIN; # WATCH OUT IF YOU CHANGE THESE!! prkey_prot = *=NEVER, ERASE=$SOPIN, READ=NONE, CRYPTO=$PIN, UPDATE=$SOPIN; exkey_prot = *=NEVER, ERASE=$SOPIN, READ=$PIN, UPDATE=$SOPIN; so-pin-flags = initialized, soPin; } } # Additional onepin option settings option onepin { macros { protected = *=$PIN, READ=NONE; mf_prot = *=NONE, CREATE=$PIN; # Allow to delete the MF p15_prot = *=$PIN, SELECT=NONE, FILES=NONE, CREATE=NONE; pin_prot = *=NEVER, WRITE=$PIN, UPDATE=$PIN; # WATCH OUT IF YOU CHANGE THESE!! prkey_prot = *=NEVER, ERASE=$PIN, READ=NONE, CRYPTO=$PIN, UPDATE=$PIN; # READ: only applies on public key exkey_prot = *=NEVER, ERASE=$PIN, READ=$PIN, UPDATE=$PIN; so-pin-flags = initialized; } } # Define reasonable limits for PINs and PUK PIN user-pin { attempts = 3; flags = initialized, needs-padding; } PIN user-puk { attempts = 5; } PIN so-pin { reference = 1; flags = $so-pin-flags; } # Additional filesystem info. # This is added to the file system info specified in the # main profile. filesystem { DF MF { ACL = $mf_prot; size = 42; # size = 2 + 2*(number of sub-files) -> 20 sub-files # There's 1 pin/key file EF pinfile { file-id = 0080; # Recommended by Setec structure = 0x22; # ISF key-file, Setcos V4.4 specific record-length = 28; size = 112; # 28 * 4 = 112 -> 1 SO + 3 user pins/puks ACL = $pin_prot; } DF PKCS15-AppDF { ACL = *=$SOPIN, SELECT=NONE, FILES=NONE, CREATE=NONE; size = 82; # size = 2 + 2*(number of sub-files) -> 40 sub-files EF PKCS15-PrKDF { file-id = 4402; size = 480; acl = $protected; } EF PKCS15-PuKDF { file-id = 4403; size = 480; acl = $protected; } EF PKCS15-CDF { file-id = 4404; size = 960; acl = $protected; } EF PKCS15-DODF { file-id = 4405; size = 480; acl = $protected; } EF template-private-key { file-id = 5100; type = internal-ef; size = 512; # enough for a 1024 bit RSA key ACL = $prkey_prot; } EF template-extractable-key { file-id = 5300; type = internal-ef; size = 512; # enough for a 1024 bit RSA key ACL = $exkey_prot; } EF template-public-key { file-id = 5200; acl = $protected; } EF template-certificate { file-id = 5500; acl = $protected; } EF template-data { file-id = 5000; structure = transparent; acl = $protected; } } } } OpenSC-0.26.1/src/pkcs15init/starcos.profile000066400000000000000000000053001474147347300205640ustar00rootroot00000000000000# # pkcs15 profile for starcos spk 2.3 # cardinfo { max-pin-length = 8; pin-encoding = ascii-numeric; pin-pad-char = 0x00; } option default { macros { so-pin-flags = initialized, needs-padding, soPin; isf_acl = WRITE=$SOPIN, CREATE=$SOPIN; df_acl = *=$SOPIN; } } option onepin { macros { so-pin-flags = initialized, needs-padding; isf_acl = WRITE=$PIN, CREATE=$PIN; df_acl = *=$PIN; } } PIN so-pin { reference = 1; flags = $so-pin-flags; } 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 { ACL = $df_acl; size = 768; # INTERNAL SECRET KEY file of the MF EF mf_isf { size = 256; ACL = $isf_acl; } EF mf_ipf { file-id = 0010; size = 256; } DF PKCS15-AppDF { ACL = $df_acl; size = 16000; # INTERNAL SECRET KEY file of the application DF # Note: if the WRITE ACL is commented out or no # sopin is specified the ACs must be activated via # 'pkcs15-init --finalize' (in this case the # AC WRITE is NEVER as the required state can't # be reached). EF p15_isf { path = 3f005015; size = 2560; ACL = $isf_acl; } EF p15_ipf { file-id = 0010; size = 1280; } 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 = 3301; structure = transparent; ACL = *=NEVER, READ=NONE, UPDATE=$PIN, ERASE=$PIN; } # private data objects are stored in transparent EFs. EF privdata { file-id = 3401; structure = transparent; ACL = *=NEVER, READ=$PIN, UPDATE=$PIN, ERASE=$PIN; } } } } } OpenSC-0.26.1/src/scconf/000077500000000000000000000000001474147347300150075ustar00rootroot00000000000000OpenSC-0.26.1/src/scconf/Makefile.am000066400000000000000000000005351474147347300170460ustar00rootroot00000000000000include $(top_srcdir)/win32/ltrc.inc MAINTAINERCLEANFILES = $(srcdir)/Makefile.in DISTCLEANFILES = lex-parse.c EXTRA_DIST = Makefile.mak dist_noinst_DATA = README.scconf lex-parse.l noinst_HEADERS = internal.h scconf.h noinst_LTLIBRARIES = libscconf.la AM_CPPFLAGS = -I$(top_srcdir)/src libscconf_la_SOURCES = scconf.c parse.c write.c sclex.c OpenSC-0.26.1/src/scconf/Makefile.mak000066400000000000000000000003421474147347300172150ustar00rootroot00000000000000TOPDIR = ..\.. TARGET = scconf.lib OBJECTS = scconf.obj parse.obj write.obj sclex.obj .SUFFIXES : .l all: $(TARGET) !INCLUDE $(TOPDIR)\win32\Make.rules.mak $(TARGET): $(OBJECTS) lib $(LIBFLAGS) /out:$(TARGET) $(OBJECTS) OpenSC-0.26.1/src/scconf/README.scconf000066400000000000000000000226441474147347300171510ustar00rootroot00000000000000A short introduction to scconf as an API and a file format ========================================================== written by Jamie Honan The scconf system is a small system library for handling scconf files. Why should anyone care about scconf format? It is a handy format for short pieces of structured data. Handy because: - it is readable, which makes support easy - it is easy to parse and write - it is extensible, you can add fields without breaking things It isn't - XML, so it doesn't need xml parsing - suitable for large amounts of data, like a database or text files It doesn't have - anything else but data. No locking, no threads etc. It has hierarchical data blocks, it has lists. Similar, but different: - .ini files. scconf is block structured, has lists and arrays - xml. xml is more complete, but requires a lot of overhead - sexp. sexp resembles lisp with it's use of parenthesis. sexp has modes for binary. scconf really doesn't have binary - yaml. yaml is larger What does it look like? ======================= Like this: transport_stream { id = 0x0009; original_network_id = 0x1000; sat_tuning_info { frequency = 12278000; symbol_rate = 30000000; polarization = 0; } service { id = 0x0064; pmt_pid = 0x0101; type = 144; name = "aGuide"; provider_name = "A"; } service { id = 0x238D; pmt_pid = 0x0623; type = 144; name = "aCar"; provider_name = "A"; } } Why doesn't it have X, why don't you use XML? ============================================= Maybe it should. Maybe XML is the answer. Maybe a database is more appropriate. It's all a trade-off. You choose. API === There are four useful structures. scconf_block, scconf_list, scconf_item, and a scconf_context. A context is similar to a file, except in memory. Within a context there is a root block. Within each block there are one or more items. Items can be sub-blocks, lists, or comments. Every item can have a name, or key. A list can have one or more values; boolean, integer or string. A context contains a root block, which contains one or more blocks. A block is : key [[,] name [[,] name ... ] ] { block_contents } block_contents is one or more block_items block_items is one of # comment string \n or key [[,] name [[,] name ... ] ] = value [[,] value ... ]]; or block Initialising and file handling ============================== Allocate scconf_context The filename can be NULL. The file is not read at this point, but in the function scconf_parse. scconf_context *scconf_new(const char *filename); Free scconf_context void scconf_free(scconf_context * config); Parse configuration Returns 1 = ok, 0 = error, -1 = error opening config file int scconf_parse(scconf_context * config); Write config to a file If the filename is NULL, use the config->filename Returns 0 = ok, else = errno int scconf_write(scconf_context * config, const char *filename); Finding items and blocks ======================== Find a block by key If the block is NULL, the root block is used 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 hierarchy, it only finds blocks in the top level of either the context (the root block) or of the block given in the block parameter (if not NULL). The block pointer returned points to data held by the context, hence the const qualifier. Find blocks by key and possibly name If the block is NULL, the root block is used The key can be used to specify what the blocks first name should be scconf_block **scconf_find_blocks(const scconf_context * config, const scconf_block * block, const char *item_name, const char *key); This function is similar to scconf_find_block above, except that an array of pointers to matched blocks is returned. Each pointer points to data held by the context. The last entry in the returned table is the null pointer. The table should be freed after use, but the individual pointers to blocks point to data held by the context. The key values for blocks is matched. If name is not NULL, the block name must also match. Get a list of values for option const scconf_list *scconf_find_list(const scconf_block * block, const char *option); Find an item that has a value (i.e. is not a block nor a comment), and return the values for that item as a list. The list is held in memory owned by the context. Return the first string of the option If no option found, return def const char *scconf_get_str(const scconf_block * block, const char *option, const char *def); This is similar to scconf_find_list, but instead of returning the whole list, just return the first value, as a string. If this is not possible, return the default value. Again the value returned is either a pointer the default value or to memory held by the context. Return the first value of the option as integer If no option found, return def int scconf_get_int(const scconf_block * block, const char *option, int def); This is similar to scconf_get_str, but an integer value is returned. Return the first value of the option as boolean If no option found, return def int scconf_get_bool(const scconf_block * block, const char *option, int def); This completes the types that can be returned by a find. For parsing blocks and items ============================ A table of scconf_entry values is used, terminated by a NULL name value. This table is passed to the routine scconf_parse_entries. This function walks the current context or block, and adds the data to the scconf_entry table entries. Sub-blocks can be walked, using SCCONF_BLOCK, and callbacks can be issued using SCCONF_CALLBACK. This is a handy method for accessing scconf data from within a program. typedef struct _scconf_entry { const char *name; * Look for blocks with this key, or check if this * block has an item with this key. Run the block * or blocks found against the rest of this entry * Stop after the first one, unless * SCCONF_ALL_BLOCKS is set in flags unsigned int type; * SCCONF_CALLBACK * parm contains a function ptr of type * int (*callback)(scconf_context* context, * scconf_block* block, * scconf_entry* entry, * int depth); * run the callback with the block found * * SCCONF_BLOCK * param contains a pointer to another entry table * use the found block against every entry * in the pointed entry table * * SCCONF_LIST * SCCONF_BOOLEAN * SCCONF_INTEGER * SCCONF_STRING * find the entry with the key given in name in * the found block. Return the value found * to parm as follows: * SCCONF_INTEGER: * if parm not NULL, then * points to integer location to put * the value * SCCONF_BOOLEAN: * if parm not NULL, then * points to integer location to put * the value * SCCONF_STRING: * if parm not NULL, then * if flag bit SCCONF_ALLOC not set * then parm points to a buffer * else * parm points to a pointer where * the pointer to an allocated * buffer should be stored. * if arg is not NULL, points * to a location where the buffer * length (size_t) is to be stored * SCCONF_LIST: * if parm not NULL, then * if flag bit SCCONF_ALLOC not set * then parm points to a location * where a pointer to the list * can be stored * else * then parm points to a location * where a pointer to a copy of list * can be stored * * unsigned int flags; * SCCONF_PRESENT * This bit is or'ed in when found * SCCONF_MANDATORY * If not found, this is a fault * SCCONF_ALLOC * C.f. type above * SCCONF_ALL_BLOCKS * C.f. name above * SCCONF_VERBOSE * For debugging void *parm; void *arg; } scconf_entry; For adding blocks and items =========================== A table of scconf_entry values is used, terminated by a NULL name value. This table is passed to the routine scconf_write_entries. This function adds the scconf_entry table entries to the current block. Sub-blocks can be added, and callbacks can be issued. This is a handy method for adding scconf data from within a program. typedef struct _scconf_entry { const char *name; * key value for blocks and items * unsigned int type; * SCCONF_CALLBACK * parm contains a function ptr of type * int (*callback)(scconf_context* context, * scconf_block* block, * scconf_entry* entry, * int depth); * * SCCONF_BLOCK * param contains a pointer to another entry table * the entry table is added as a block to the * current block, with name as the key, and * arg is a list of names * * SCCONF_LIST * SCCONF_BOOLEAN * SCCONF_INTEGER * SCCONF_STRING * these add key=value pairs to the current * block. The value is in parm. * unsigned int flags; * SCCONF_PRESENT * This bit is or'ed in when item added void *parm; void *arg; } scconf_entry; OpenSC-0.26.1/src/scconf/internal.h000066400000000000000000000033041474147347300167740ustar00rootroot00000000000000/* * $Id$ * * Copyright (C) 2002 * Antti Tapaninen * * 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 _SCCONF_INTERNAL_H #define _SCCONF_INTERNAL_H #include "scconf.h" #ifdef __cplusplus extern "C" { #endif #define TOKEN_TYPE_COMMENT 0 #define TOKEN_TYPE_NEWLINE 1 #define TOKEN_TYPE_STRING 2 #define TOKEN_TYPE_PUNCT 3 #define DEPTH_LIMIT 16 typedef struct _scconf_parser { scconf_context *config; scconf_block *block; scconf_item *last_item, *current_item; char *key; scconf_list *name; int state; int last_token_type; int line; 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 } #endif #endif OpenSC-0.26.1/src/scconf/lex-parse.l000066400000000000000000000037721474147347300170750ustar00rootroot00000000000000%{ /* * $Id$ * * Copyright (C) 2002 * Antti Tapaninen * * 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 #include "scconf.h" #include "internal.h" static scconf_parser *parser; %} %option noyywrap %option nounput %% "#"[^\r\n]* scconf_parse_token(parser, TOKEN_TYPE_COMMENT, yytext); \n scconf_parse_token(parser, TOKEN_TYPE_NEWLINE, NULL); [ \t\r]+ /* eat up whitespace */ [,{}=;] scconf_parse_token(parser, TOKEN_TYPE_PUNCT, yytext); \"[^\"\n\r]*\r*[\"\n] scconf_parse_token(parser, TOKEN_TYPE_STRING, yytext); [^;, \t\r\n]+ scconf_parse_token(parser, TOKEN_TYPE_STRING, yytext); %% #ifndef YY_CURRENT_BUFFER_LVALUE # define YY_CURRENT_BUFFER_LVALUE yy_current_buffer #endif static void do_lex(scconf_parser *p) { parser = p; yylex(); #if 1 /* For non-reentrant C scanner only. */ if (YY_CURRENT_BUFFER) { yy_delete_buffer(YY_CURRENT_BUFFER); YY_CURRENT_BUFFER_LVALUE = NULL; yy_init = 1; yy_start = 0; } #endif } int scconf_lex_parse(scconf_parser *p, const char *filename) { yyin = fopen(filename, "r"); if (yyin == NULL) return 0; do_lex(p); fclose(yyin); yyin = NULL; return 1; } int scconf_lex_parse_string(scconf_parser *p, const char *conf_string) { yy_scan_string(conf_string); do_lex(p); return 1; } OpenSC-0.26.1/src/scconf/parse.c000066400000000000000000000242711474147347300162730ustar00rootroot00000000000000/* * $Id$ * * Copyright (C) 2002 * Antti Tapaninen * * 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 #ifdef HAVE_STRINGS_H #include #endif #include #include "common/compat_strlcpy.h" #include "internal.h" #include "scconf.h" #define STATE_NAME 0x01 #define STATE_VALUE 0x02 #define STATE_SET 0x10 static scconf_item *scconf_get_last_item(scconf_block *root) { scconf_block *block = root; scconf_item *item; for (item = root->items; item; item = item->next) { if (!item->next) { return item; } } return block->items; } static void scconf_parse_error(scconf_parser * parser, const char *error) { /* FIXME: save the error somewhere */ parser->error = 1; snprintf(parser->emesg, sizeof(parser->emesg), "Line %d: %s\n", parser->line, error); } static void scconf_parse_error_not_expect(scconf_parser * parser, const char *token) { /* FIXME: save the error somewhere */ parser->error = 1; snprintf(parser->emesg, sizeof(parser->emesg), "Line %d: not expecting '%s'\n", parser->line, token); } static void scconf_parse_warning_expect(scconf_parser * parser, const char *token) { /* FIXME: save the warnings somewhere */ parser->warnings = 1; snprintf(parser->emesg, sizeof(parser->emesg), "Line %d: missing '%s', ignoring\n", parser->line, token); } static scconf_item *scconf_item_find(scconf_parser * parser) { scconf_item *item; for (item = parser->block->items; item; item = item->next) { if (item && item->type == SCCONF_ITEM_TYPE_VALUE && item->key && parser->key && strcasecmp(item->key, parser->key) == 0) { return item; } } return item; } static scconf_item *scconf_item_add_internal(scconf_parser * parser, int type) { scconf_item *item; if (type == SCCONF_ITEM_TYPE_VALUE) { /* if item with same key already exists, use it */ item = scconf_item_find(parser); if (item) { free(parser->key); parser->key = NULL; parser->current_item = item; return item; } } item = calloc(1, sizeof(scconf_item)); if (!item) { return NULL; } item->type = type; item->key = parser->key; parser->key = NULL; if (parser->last_item) { parser->last_item->next = item; } else { parser->block->items = item; } parser->current_item = parser->last_item = item; return item; } scconf_item *scconf_item_add(scconf_context * config, scconf_block * block, scconf_item * item, int type, const char *key, const void *data) { scconf_parser parser; scconf_block *dst = NULL; if (!config && !block) return NULL; if (!data) return NULL; memset(&parser, 0, sizeof(scconf_parser)); parser.config = config ? config : NULL; parser.key = key ? strdup(key) : NULL; parser.block = block ? block : config->root; parser.name = NULL; parser.last_item = scconf_get_last_item(parser.block); parser.current_item = item; if (type == SCCONF_ITEM_TYPE_BLOCK) { scconf_block_copy((const scconf_block *) data, &dst); scconf_list_copy(dst->name, &parser.name); } 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); break; case SCCONF_ITEM_TYPE_BLOCK: if (!dst) return NULL; dst->parent = parser.block; parser.current_item->value.block = dst; scconf_list_destroy(parser.name); break; case SCCONF_ITEM_TYPE_VALUE: scconf_list_copy((const scconf_list *) data, &parser.current_item->value.list); break; } } else { /* FIXME is it an error if item is NULL? */ free(parser.key); parser.key = NULL; } return parser.current_item; } static void scconf_block_add_internal(scconf_parser * parser) { scconf_block *block; scconf_item *item; item = scconf_item_add_internal(parser, SCCONF_ITEM_TYPE_BLOCK); if (!item) { return; } block = calloc(1, sizeof(scconf_block)); if (!block) { return; } block->parent = parser->block; item->value.block = block; if (!parser->name) { scconf_list_add(&parser->name, ""); } block->name = parser->name; parser->name = NULL; parser->block = block; parser->last_item = NULL; } scconf_block *scconf_block_add(scconf_context * config, scconf_block * block, const char *key, const scconf_list *name) { scconf_parser parser; if (!config) return NULL; memset(&parser, 0, sizeof(scconf_parser)); parser.config = config; parser.key = key ? strdup(key) : NULL; parser.block = block ? block : config->root; scconf_list_copy(name, &parser.name); parser.last_item = scconf_get_last_item(parser.block); parser.current_item = parser.block->items; scconf_block_add_internal(&parser); return parser.block; } static void scconf_parse_parent(scconf_parser * parser) { parser->block = parser->block->parent; parser->last_item = parser->block->items; if (parser->last_item) { while (parser->last_item->next) { parser->last_item = parser->last_item->next; } } } static void scconf_parse_reset_state(scconf_parser * parser) { if (parser) { if (parser->key) { free(parser->key); } scconf_list_destroy(parser->name); parser->key = NULL; parser->name = NULL; parser->state = 0; } } 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; size_t len; if (parser->error) { /* fatal error */ return; } switch (token_type) { case TOKEN_TYPE_NEWLINE: parser->line++; if (parser->last_token_type != TOKEN_TYPE_NEWLINE) { break; } /* fall through - treat empty lines as comments */ case TOKEN_TYPE_COMMENT: item = scconf_item_add_internal(parser, SCCONF_ITEM_TYPE_COMMENT); if (!item) { return; } item->value.comment = token ? strdup(token) : NULL; break; case TOKEN_TYPE_STRING: { char *stoken = NULL; if ((parser->state & (STATE_VALUE | STATE_SET)) == (STATE_VALUE | STATE_SET)) { scconf_parse_warning_expect(parser, ";"); scconf_parse_reset_state(parser); } if (token && *token == '"') { /* quoted string, remove them */ token++; len = strlen(token); if (len < 1 || token[len - 1] != '"') { scconf_parse_warning_expect(parser, "\""); } else { /* stoken */ stoken = strdup(token); if (stoken) { stoken[len - 1] = '\0'; } } } if (!stoken) { stoken = token ? strdup(token) : NULL; } if (parser->state == 0) { /* key */ parser->key = stoken ? strdup(stoken) : NULL; parser->state = STATE_NAME; } else if (parser->state == STATE_NAME) { /* name */ parser->state |= STATE_SET; scconf_list_add(&parser->name, stoken); } 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, stoken); } else { /* error */ scconf_parse_error_not_expect(parser, stoken); } if (stoken) { free(stoken); } stoken = NULL; } break; case TOKEN_TYPE_PUNCT: switch (*token) { case '{': if ((parser->state & STATE_NAME) == 0) { 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) { scconf_parse_error_not_expect(parser, "}"); break; } /* foo = bar } */ scconf_parse_warning_expect(parser, ";"); scconf_parse_reset_state(parser); } if (!parser->block->parent) { /* too many '}' */ scconf_parse_error(parser, "missing matching '{'"); break; } scconf_parse_parent(parser); break; case ',': if ((parser->state & (STATE_NAME | STATE_VALUE)) == 0) { scconf_parse_error_not_expect(parser, ","); } parser->state &= ~STATE_SET; break; case '=': if ((parser->state & STATE_NAME) == 0) { scconf_parse_error_not_expect(parser, "="); break; } scconf_item_add_internal(parser, SCCONF_ITEM_TYPE_VALUE); parser->state = STATE_VALUE; break; case ';': scconf_parse_reset_state(parser); break; default: snprintf(parser->emesg, sizeof(parser->emesg), "Line %d: bad token ignoring\n", parser->line); } break; } parser->last_token_type = token_type; } int scconf_parse(scconf_context * config) { static char buffer[256]; scconf_parser p; int r = 1; memset(&p, 0, sizeof(p)); 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), "Unable to open \"%s\": %s", config->filename, strerror(errno)); r = -1; } else if (p.error) { strlcpy(buffer, p.emesg, sizeof(buffer)); r = 0; } else { r = 1; } if (r <= 0) config->errmsg = buffer; return r; } int scconf_parse_string(scconf_context * config, const char *string) { static char buffer[256]; scconf_parser p; int r; memset(&p, 0, sizeof(p)); 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), "Failed to parse configuration string"); r = -1; } else if (p.error) { strlcpy(buffer, p.emesg, sizeof(buffer)); r = 0; } else { r = 1; } scconf_parse_reset_state(&p); if (r <= 0) config->errmsg = buffer; return r; } OpenSC-0.26.1/src/scconf/scconf.c000066400000000000000000000212411474147347300164260ustar00rootroot00000000000000/* * $Id$ * * Copyright (C) 2002 * Antti Tapaninen * * 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 #ifdef HAVE_STRINGS_H #include #endif #include #include #include "scconf.h" scconf_context *scconf_new(const char *filename) { scconf_context *config; config = calloc(1, sizeof(scconf_context)); if (!config) { return NULL; } config->filename = filename ? strdup(filename) : NULL; config->root = calloc(1, sizeof(scconf_block)); if (!config->root) { if (config->filename) { free(config->filename); } free(config); return NULL; } return config; } void scconf_free(scconf_context * config) { if (config) { scconf_block_destroy(config->root); if (config->filename) { free(config->filename); } free(config); } } const scconf_block *scconf_find_block(const scconf_context * config, const scconf_block * block, const char *item_name) { scconf_item *item; if (!block) { block = config->root; } if (!item_name) { return NULL; } for (item = block->items; item; item = item->next) { if (item->type == SCCONF_ITEM_TYPE_BLOCK && strcasecmp(item_name, item->key) == 0) { return item->value.block; } } return NULL; } scconf_block **scconf_find_blocks(const scconf_context * config, const scconf_block * block, const char *item_name, const char *key) { scconf_block **blocks = NULL, **tmp; int alloc_size, size; scconf_item *item; if (!block) { block = config->root; } if (!item_name) { return NULL; } size = 0; alloc_size = 10; tmp = (scconf_block **) realloc(blocks, sizeof(scconf_block *) * alloc_size); if (!tmp) { free(blocks); return NULL; } blocks = tmp; for (item = block->items; item; item = item->next) { if (item->type == SCCONF_ITEM_TYPE_BLOCK && strcasecmp(item_name, item->key) == 0) { if (!item->value.block) continue; if (key && strcasecmp(key, item->value.block->name->data)) { continue; } if (size + 1 >= alloc_size) { alloc_size *= 2; tmp = (scconf_block **) realloc(blocks, sizeof(scconf_block *) * alloc_size); if (!tmp) { free(blocks); return NULL; } blocks = tmp; } blocks[size++] = item->value.block; } } blocks[size] = NULL; return blocks; } const scconf_list *scconf_find_list(const scconf_block * block, const char *option) { scconf_item *item; if (!block) return NULL; for (item = block->items; item; item = item->next) if (item->type == SCCONF_ITEM_TYPE_VALUE && strcasecmp(option, item->key) == 0) return item->value.list; return NULL; } const char *scconf_get_str(const scconf_block * block, const char *option, const char *def) { const scconf_list *list; list = scconf_find_list(block, option); if (!list) return def; /* ignore non 'auto-configured' values */ if (*list->data == '@' && *(list->data + strlen(list->data) - 1) == '@') return def; return list->data; } 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); 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) { const scconf_list *list; list = scconf_find_list(block, option); if (!list) { return def; } return toupper((int) *list->data) == 'T' || toupper((int) *list->data) == 'Y'; } const char *scconf_put_str(scconf_block * block, const char *option, const char *value) { scconf_list *list = NULL; scconf_list_add(&list, value); scconf_item_add(NULL, block, NULL, SCCONF_ITEM_TYPE_VALUE, option, list); scconf_list_destroy(list); return value; } int scconf_put_int(scconf_block * block, const char *option, int value) { char *str; str = malloc(64); if (!str) { return value; } snprintf(str, 64, "%i", value); scconf_put_str(block, option, str); free(str); return value; } int scconf_put_bool(scconf_block * block, const char *option, int value) { scconf_put_str(block, option, !value ? "false" : "true"); return value; } scconf_item *scconf_item_copy(const scconf_item * src, scconf_item ** dst) { scconf_item *ptr, *_dst = NULL, *next = NULL; next = calloc(1, sizeof(scconf_item)); if (!next) { return NULL; } ptr = next; _dst = next; while (src) { if (!next) { next = calloc(1, sizeof(scconf_item)); if (!next) { scconf_item_destroy(ptr); return NULL; } _dst->next = next; } next->type = src->type; switch (src->type) { case SCCONF_ITEM_TYPE_COMMENT: next->value.comment = src->value.comment ? strdup(src->value.comment) : NULL; break; case SCCONF_ITEM_TYPE_BLOCK: scconf_block_copy(src->value.block, &next->value.block); break; case SCCONF_ITEM_TYPE_VALUE: scconf_list_copy(src->value.list, &next->value.list); break; } next->key = src->key ? strdup(src->key) : NULL; _dst = next; next = NULL; src = src->next; } *dst = ptr; return ptr; } void scconf_item_destroy(scconf_item * item) { scconf_item *next; while (item) { next = item->next; switch (item->type) { case SCCONF_ITEM_TYPE_COMMENT: if (item->value.comment) { free(item->value.comment); } item->value.comment = NULL; break; case SCCONF_ITEM_TYPE_BLOCK: scconf_block_destroy(item->value.block); break; case SCCONF_ITEM_TYPE_VALUE: scconf_list_destroy(item->value.list); break; } if (item->key) { free(item->key); } item->key = NULL; free(item); item = next; } } scconf_block *scconf_block_copy(const scconf_block * src, scconf_block ** dst) { if (src) { scconf_block *_dst = NULL; _dst = calloc(1, sizeof(scconf_block)); if (!_dst) { return NULL; } memset(_dst, 0, sizeof(scconf_block)); if (src->name) { scconf_list_copy(src->name, &_dst->name); } if (src->items) { scconf_item_copy(src->items, &_dst->items); } *dst = _dst; return _dst; } return NULL; } void scconf_block_destroy(scconf_block * block) { if (block) { scconf_list_destroy(block->name); scconf_item_destroy(block->items); free(block); } } scconf_list *scconf_list_add(scconf_list ** list, const char *value) { scconf_list *rec, **tmp; rec = calloc(1, sizeof(scconf_list)); if (!rec) { return NULL; } rec->data = value ? strdup(value) : NULL; if (!*list) { *list = rec; } else { for (tmp = list; *tmp; tmp = &(*tmp)->next); *tmp = rec; } return rec; } scconf_list *scconf_list_copy(const scconf_list * src, scconf_list ** dst) { scconf_list *next; while (src) { next = src->next; scconf_list_add(dst, src->data); src = next; } return *dst; } void scconf_list_destroy(scconf_list * list) { scconf_list *next; while (list) { next = list->next; if (list->data) { free(list->data); } free(list); list = next; } } int scconf_list_array_length(const scconf_list * list) { int len = 0; while (list) { len++; list = list->next; } return len; } int scconf_list_strings_length(const scconf_list * list) { int len = 0; while (list && list->data) { len += strlen(list->data) + 1; list = list->next; } return len; } const char **scconf_list_toarray(const scconf_list * list) { const scconf_list * lp = list; const char **tp; int len = 0; while (lp) { len++; lp = lp->next; } tp = malloc(sizeof(char *) * (len + 1)); if (!tp) return tp; lp = list; len = 0; while (lp) { tp[len] = lp->data; len++; lp = lp->next; } tp[len] = NULL; return tp; } char *scconf_list_strdup(const scconf_list * list, const char *filler) { char *buf = NULL; int len = 0; if (!list) { return NULL; } len = scconf_list_strings_length(list); if (filler) { len += scconf_list_array_length(list) * (strlen(filler) + 1); } if (len == 0) return NULL; buf = calloc(1, len); if (!buf) { return NULL; } while (list && list->data) { strcat(buf, list->data); if (filler) { strcat(buf, filler); } list = list->next; } if (filler) buf[strlen(buf) - strlen(filler)] = '\0'; return buf; } OpenSC-0.26.1/src/scconf/scconf.h000066400000000000000000000133331474147347300164360ustar00rootroot00000000000000/* * $Id$ * * Copyright (C) 2002 * Antti Tapaninen * * Originally based on source by Timo Sirainen * * 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_CONF_H #define _SC_CONF_H #ifdef __cplusplus extern "C" { #endif #define SCCONF_BOOLEAN 11 #define SCCONF_INTEGER 12 #define SCCONF_STRING 13 typedef struct _scconf_block scconf_block; typedef struct _scconf_list { struct _scconf_list *next; char *data; } scconf_list; #define SCCONF_ITEM_TYPE_COMMENT 0 /* key = NULL, comment */ #define SCCONF_ITEM_TYPE_BLOCK 1 /* key = key, block */ #define SCCONF_ITEM_TYPE_VALUE 2 /* key = key, list */ typedef struct _scconf_item { struct _scconf_item *next; int type; char *key; union { char *comment; scconf_block *block; scconf_list *list; } value; } scconf_item; struct _scconf_block { scconf_block *parent; scconf_list *name; scconf_item *items; }; typedef struct { char *filename; int debug; scconf_block *root; char *errmsg; } scconf_context; /* Allocate scconf_context * The filename can be NULL */ extern scconf_context *scconf_new(const char *filename); /* Free scconf_context */ extern void scconf_free(scconf_context * config); /* Parse configuration * Returns 1 = ok, 0 = error, -1 = error opening config file */ extern int scconf_parse(scconf_context * config); /* Parse a static configuration string * Returns 1 = ok, 0 = error */ extern int scconf_parse_string(scconf_context * config, const char *string); /* Write config to a file * If the filename is NULL, use the config->filename * Returns 0 = ok, else = errno */ extern int scconf_write(scconf_context * config, const char *filename); /* Find a block by the item_name * If the block is NULL, the root block is used */ extern const scconf_block *scconf_find_block(const scconf_context * config, const scconf_block * block, const char *item_name); /* Find blocks by the item_name * If the block is NULL, the root block is used * The key can be used to specify what the blocks first name should be */ extern scconf_block **scconf_find_blocks(const scconf_context * config, const scconf_block * block, const char *item_name, const char *key); /* Get a list of values for option */ extern const scconf_list *scconf_find_list(const scconf_block * block, const char *option); /* Return the first string of the option * If no option found, return def */ extern const char *scconf_get_str(const scconf_block * block, const char *option, const char *def); /* Return the first value of the option as integer * If no option found, return def */ extern int scconf_get_int(const scconf_block * block, const char *option, int def); /* Return the first value of the option as boolean * If no option found, return def */ extern int scconf_get_bool(const scconf_block * block, const char *option, int def); /* Write value to a block as a string */ extern const char *scconf_put_str(scconf_block * block, const char *option, const char *value); /* Write value to a block as an integer */ extern int scconf_put_int(scconf_block * block, const char *option, int value); /* Write value to a block as a boolean */ extern int scconf_put_bool(scconf_block * block, const char *option, int value); /* Add block structure * If the block is NULL, the root block is used */ extern scconf_block *scconf_block_add(scconf_context * config, scconf_block * block, const char *key, const scconf_list *name); /* Copy block structure (recursive) */ extern scconf_block *scconf_block_copy(const scconf_block * src, scconf_block ** dst); /* Free block structure (recursive) */ extern void scconf_block_destroy(scconf_block * block); /* Add item to block structure * If the block is NULL, the root block is used */ extern scconf_item *scconf_item_add(scconf_context * config, scconf_block * block, scconf_item * item, int type, const char *key, const void *data); /* Copy item structure (recursive) */ extern scconf_item *scconf_item_copy(const scconf_item * src, scconf_item ** dst); /* Free item structure (recursive) */ extern void scconf_item_destroy(scconf_item * item); /* Add a new value to the list */ extern scconf_list *scconf_list_add(scconf_list ** list, const char *value); /* Copy list structure */ extern scconf_list *scconf_list_copy(const scconf_list * src, scconf_list ** dst); /* Free list structure */ extern void scconf_list_destroy(scconf_list * list); /* Return the length of an list array */ extern int scconf_list_array_length(const scconf_list * list); /* Return the combined length of the strings on all arrays */ extern int scconf_list_strings_length(const scconf_list * list); /* Return an allocated string that contains all * the strings in a list separated by the filler * The filler can be NULL */ extern char *scconf_list_strdup(const scconf_list * list, const char *filler); /* Returns an allocated array of const char *pointers to * list elements. * Last pointer is NULL * Array must be freed, but pointers to strings belong to scconf_list */ extern const char **scconf_list_toarray(const scconf_list * list); #ifdef __cplusplus } #endif #endif OpenSC-0.26.1/src/scconf/sclex.c000066400000000000000000000102611474147347300162710ustar00rootroot00000000000000/* * $Id$ * * Copyright (C) 2003 * Jamie Honan * * 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 #ifdef HAVE_STRINGS_H #include #endif #include "scconf.h" #include "internal.h" typedef struct { char *buf; size_t bufmax; size_t bufcur; int saved_char; const char *saved_string; FILE *fp; } BUFHAN; static void buf_init(BUFHAN * bp, FILE * fp, const char *saved_string) { bp->fp = fp; bp->saved_char = 0; bp->buf = malloc(256); if (bp->buf) { bp->bufmax = 256; bp->buf[0] = '\0'; } else bp->bufmax = 0; bp->bufcur = 0; bp->saved_string = saved_string; } static void buf_addch(BUFHAN * bp, char ch) { if (bp->bufcur + 1 >= bp->bufmax) { char *p = (char *) realloc(bp->buf, bp->bufmax + 256); if (!p) return; bp->bufmax += 256; bp->buf = p; } if (bp->buf) { bp->buf[bp->bufcur++] = ch; bp->buf[bp->bufcur] = '\0'; } } static int buf_nextch(BUFHAN * bp) { int saved; if (bp->saved_char) { saved = bp->saved_char; bp->saved_char = 0; return saved; } if (bp->saved_string) { if (*(bp->saved_string) == '\0') return EOF; saved = (unsigned char) (*(bp->saved_string++)); return saved; } else { saved = fgetc(bp->fp); return saved; } } static void buf_finished(BUFHAN * bp) { if (bp->buf) { free(bp->buf); bp->buf = NULL; } } static void buf_eat_till(BUFHAN * bp, char start, const char *end) { int i; if (start) { buf_addch(bp, start); } while (1) { i = buf_nextch(bp); if (i == EOF) return; if (strchr(end, i)) { bp->saved_char = i; return; } buf_addch(bp, (char) i); } } static void buf_zero(BUFHAN * bp) { bp->bufcur = 0; bp->buf[0] = '\0'; } static int scconf_lex_engine(scconf_parser * parser, BUFHAN * bp) { int this_char; while (1) { switch (this_char = buf_nextch(bp)) { case '#': /* comment till end of line */ buf_eat_till(bp, '#', "\r\n"); scconf_parse_token(parser, TOKEN_TYPE_COMMENT, bp->buf); buf_zero(bp); continue; case '\n': scconf_parse_token(parser, TOKEN_TYPE_NEWLINE, NULL); continue; case ' ': case '\t': case '\r': /* eat up whitespace */ 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 ';': buf_addch(bp, (char) this_char); scconf_parse_token(parser, TOKEN_TYPE_PUNCT, bp->buf); buf_zero(bp); continue; case '"': buf_eat_till(bp, (char) this_char, "\"\r\n"); buf_addch(bp, (char) buf_nextch(bp)); scconf_parse_token(parser, TOKEN_TYPE_STRING, bp->buf); buf_zero(bp); continue; case EOF: break; default: buf_eat_till(bp, (char) this_char, ";, \t\r\n"); scconf_parse_token(parser, TOKEN_TYPE_STRING, bp->buf); buf_zero(bp); continue; } break; } buf_finished(bp); return 1; } int scconf_lex_parse(scconf_parser * parser, const char *filename) { FILE *fp; BUFHAN bhan; int ret; fp = fopen(filename, "r"); if (!fp) { parser->error = 1; snprintf(parser->emesg, sizeof(parser->emesg), "File %s can't be opened\n", filename); return 0; } buf_init(&bhan, fp, (char *) NULL); ret = scconf_lex_engine(parser, &bhan); fclose(fp); return ret; } int scconf_lex_parse_string(scconf_parser * parser, const char *string) { BUFHAN bhan; int ret; buf_init(&bhan, (FILE *) NULL, string); ret = scconf_lex_engine(parser, &bhan); return ret; } OpenSC-0.26.1/src/scconf/write.c000066400000000000000000000105341474147347300163100ustar00rootroot00000000000000/* * $Id$ * * Copyright (C) 2002 * Antti Tapaninen * * 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 #include "scconf.h" #define INDENT_CHAR '\t' #define INDENT_LEVEL 1 typedef struct { FILE *f; int indent_char; int indent_pos; int indent_level; int error; } scconf_writer; static void write_line(scconf_writer * writer, const char *data) { int i; if (writer->error) { return; } if (!((data) == NULL || (data)[0] == '\0')) { for (i = 0; i < writer->indent_pos; i++) { fputc(writer->indent_char, writer->f); } fputs(data, writer->f); } if (fputc('\n', writer->f) == EOF) { writer->error = errno; } } static int string_need_quotes(const char *str) { /* quote only if there's any non-normal characters */ while (*str != '\0') { if (!isalnum((int) ((unsigned char) *str)) && *str != '!' && *str != '.' && *str != '/') { return 1; } str++; } return 0; } static char *scconf_list_get_string(scconf_list * list) { char *buffer = NULL, *tmp; size_t datalen, len, alloc_len, quote; if (!list) { return strdup(""); } len = 0; alloc_len = 1024; tmp = (char *) realloc(buffer, alloc_len); if (!tmp) { free(buffer); return strdup(""); } buffer = tmp; memset(buffer, 0, alloc_len); while (list) { datalen = strlen(list->data); if (len + datalen + 4 > alloc_len) { alloc_len += datalen + 2; tmp = (char *) realloc(buffer, alloc_len); if (!tmp) { free(buffer); return strdup(""); } buffer = tmp; } if (len != 0) { memcpy(buffer + len, ", ", 2); len += 2; } quote = string_need_quotes(list->data); if (quote) { buffer[len++] = '"'; } memcpy(buffer + len, list->data, datalen); len += datalen; if (quote) { buffer[len++] = '"'; } list = list->next; } buffer[len] = '\0'; return buffer; } static void scconf_write_items(scconf_writer * writer, const scconf_block * block) { scconf_block *subblock; scconf_item *item; char *data = NULL, *name = NULL; size_t datalen; for (item = block->items; item; item = item->next) { switch (item->type) { case SCCONF_ITEM_TYPE_COMMENT: write_line(writer, item->value.comment); break; case SCCONF_ITEM_TYPE_BLOCK: subblock = item->value.block; if (!subblock) { fprintf(stderr, "scconf_write_items: Skipping invalid block!\n"); continue; } /* header */ name = scconf_list_get_string(subblock->name); datalen = strlen(item->key) + strlen(name) + 6; data = malloc(datalen); if (!data) { free(name); continue; } snprintf(data, datalen, "%s %s {", item->key, name); write_line(writer, data); free(data); free(name); /* items */ writer->indent_pos += writer->indent_level; scconf_write_items(writer, subblock); writer->indent_pos -= writer->indent_level; /* footer */ write_line(writer, "}"); break; case SCCONF_ITEM_TYPE_VALUE: name = scconf_list_get_string(item->value.list); datalen = strlen(item->key) + strlen(name) + 6; data = malloc(datalen); if (!data) { free(name); continue; } snprintf(data, datalen, "%s = %s;", item->key, name); write_line(writer, data); free(data); free(name); break; } } } int scconf_write(scconf_context * config, const char *filename) { scconf_writer writer; if (!filename) { filename = config->filename; } writer.f = fopen(filename, "w"); if (!writer.f) { return errno; } writer.indent_char = INDENT_CHAR; writer.indent_pos = 0; writer.indent_level = INDENT_LEVEL; writer.error = 0; scconf_write_items(&writer, config->root); fclose(writer.f); return writer.error; } OpenSC-0.26.1/src/sm/000077500000000000000000000000001474147347300141535ustar00rootroot00000000000000OpenSC-0.26.1/src/sm/Makefile.am000066400000000000000000000014051474147347300162070ustar00rootroot00000000000000# Process this file with automake to create Makefile.in MAINTAINERCLEANFILES = Makefile.in EXTRA_DIST = Makefile.mak noinst_LTLIBRARIES = libsmiso.la libsmeac.la noinst_HEADERS = \ sm-iso-internal.h \ sm-iso.h \ sm-eac.h if ENABLE_OPENSSL noinst_LTLIBRARIES += libsm.la endif noinst_HEADERS += sm-common.h 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 libsmeac_la_SOURCES = sm-eac.c libsmeac_la_LIBADD = $(OPENPACE_LIBS) $(OPENSSL_LIBS) libsmiso.la libsmeac_la_CFLAGS = $(OPENPACE_CFLAGS) $(OPENSSL_CFLAGS) -I$(top_srcdir)/src OpenSC-0.26.1/src/sm/Makefile.mak000066400000000000000000000010171474147347300163610ustar00rootroot00000000000000TOPDIR = ..\.. TARGET = libsm.lib OBJECTS = sm-common.obj TARGET1 = libsmiso.lib OBJECTS1 = sm-iso.obj TARGET2 = libsmeac.lib OBJECTS2 = sm-eac.obj all: $(TARGET) $(TARGET1) $(TARGET2) !INCLUDE $(TOPDIR)\win32\Make.rules.mak !IF "$(OPENSSL_DEF)" == "/DENABLE_OPENSSL" $(TARGET): $(OBJECTS) lib $(LIBFLAGS) /out:$(TARGET) $(OBJECTS) !ELSE $(TARGET): !ENDIF $(TARGET1): $(OBJECTS1) lib $(LIBFLAGS) /out:$(TARGET1) $(OBJECTS1) $(TARGET2): $(OBJECTS2) lib $(LIBFLAGS) /out:$(TARGET2) $(OBJECTS2) OpenSC-0.26.1/src/sm/sm-common.c000066400000000000000000000403401474147347300162250ustar00rootroot00000000000000/* * sm-common.c: Common cryptographic procedures related to * Secure Messaging * * Copyright (C) 2010 Viktor Tarasov * OpenTrust * * 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 #include #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include #include #ifndef ENABLE_OPENSSL #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 =((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 =((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)))); \ } \ } #define l2c(l,c) (*((c)++)=(unsigned char)(((l) )&0xff), \ *((c)++)=(unsigned char)(((l)>> 8L)&0xff), \ *((c)++)=(unsigned char)(((l)>>16L)&0xff), \ *((c)++)=(unsigned char)(((l)>>24L)&0xff)) /* * Inspired by or taken from OpenSSL crypto/des/cbc3_enc.c */ static void 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; sm_des_cblock icv_out; memset(&icv_out, 0, sizeof(icv_out)); if (enc == DES_ENCRYPT) { DES_cbc_encrypt((unsigned char*)input, (unsigned char*)output,length,ks1,iv,enc); DES_cbc_encrypt((unsigned char*)output, (unsigned char*)output,l8,ks2,iv,!enc); DES_cbc_encrypt((unsigned char*)output, (unsigned char*)output,l8,ks1,iv,enc); if ((unsigned)length >= sizeof(sm_des_cblock)) memcpy(icv_out,output[off],sizeof(sm_des_cblock)); } else { 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, (unsigned char*)output,l8,ks2,iv,!enc); DES_cbc_encrypt((unsigned char*)output, (unsigned char*)output,length,ks1,iv,enc); } memcpy(*iv,icv_out,sizeof(sm_des_cblock)); } #endif 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 long l=length; 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); for (; l>8; l-=8) { if (l >= 16) { c2l(in,tin0); c2l(in,tin1); } else c2ln(in,tin0,tin1,l); tin0^=tout0; tin[0]=tin0; tin1^=tout1; tin[1]=tin1; DES_encrypt1((unsigned int *)tin, &ks, DES_ENCRYPT); tout0=tin[0]; tout1=tin[1]; } if (l == 8) { c2l(in,tin0); c2l(in,tin1); } else c2ln(in,tin0,tin1,l); tin0^=tout0; tin[0]=tin0; tin1^=tout1; tin[1]=tin1; DES_encrypt3((unsigned int *)tin, &ks, &ks2, &ks); tout1=tin[1]; if (out != NULL) { l2c(tout0,out); l2c(tout1,out); } /* Transform the data in tout1 so that it will match the return value that the MIT Kerberos mit_des_cbc_cksum API returns. */ tout1 = ((tout1 >> 24L) & 0x000000FF) | ((tout1 >> 8L) & 0x0000FF00) | ((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 } 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 long l=length; 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); for (; l>0; l-=8) { if (l >= 8) { c2l(in,tin0); c2l(in,tin1); } else c2ln(in,tin0,tin1,l); tin0^=tout0; tin[0]=tin0; tin1^=tout1; tin[1]=tin1; DES_encrypt3((unsigned int *)tin, &ks, &ks2, &ks); /* fix 15/10/91 eay - thanks to keithr@sco.COM */ tout0=tin[0]; tout1=tin[1]; } if (out != NULL) { l2c(tout0,out); l2c(tout1,out); } /* Transform the data in tout1 so that it will match the return value that the MIT Kerberos mit_des_cbc_cksum API returns. */ tout1 = ((tout1 >> 24L) & 0x000000FF) | ((tout1 >> 8L) & 0x0000FF00) | ((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(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; 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; *out = malloc(*out_len); if (!(*out)) return -1; #if OPENSSL_VERSION_NUMBER < 0x30000000L memcpy(&kk, key, 8); memcpy(&k2, key + 8, 8); DES_set_key_unchecked(&kk,&ks); DES_set_key_unchecked(&k2,&ks2); for (ii=0; ii= 0; ii--) { *(ssc + ii) += 1; if (*(ssc + ii) != 0) break; } } OpenSC-0.26.1/src/sm/sm-common.h000066400000000000000000000036111474147347300162320ustar00rootroot00000000000000/* * sm-commot.h: Common SM cryptographic procedures * * Copyright (C) 2013 Viktor Tarasov * * 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 _SM_COMMON_H #define _SM_COMMON_H #ifdef __cplusplus extern "C" { #endif #include #include #include "libopensc/sm.h" 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, unsigned char **out, size_t *out_len, int not_force_pad); int sm_decrypt_des_cbc3(struct sc_context *ctx, unsigned char *key, unsigned char *data, size_t data_len, unsigned char **out, size_t *out_len); void sm_incr_ssc(unsigned char *ssc, size_t ssc_len); #ifdef __cplusplus } #endif #endif OpenSC-0.26.1/src/sm/sm-eac.c000066400000000000000000001452221474147347300154720ustar00rootroot00000000000000/* * Copyright (C) 2011-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 "sm/sm-iso.h" #include "libopensc/asn1.h" #include "libopensc/log.h" #include "libopensc/opensc.h" #include "sm-eac.h" #include #include #ifdef ENABLE_OPENSSL #include #endif char eac_default_flags = 0; #define ISO_MSE 0x22 #if defined(ENABLE_OPENPACE) && defined(ENABLE_SM) #include #include #include #include #include #include #include #include #include #include #include /** @brief EAC secure messaging context */ struct eac_sm_ctx { /** @brief EAC context */ EAC_CTX *ctx; /** @brief picc's compressed ephemeral public key of PACE */ BUF_MEM *id_icc; /** @brief PCD's compressed ephemeral public key of CA */ BUF_MEM *eph_pub_key; /** @brief Auxiliary Data */ BUF_MEM *auxiliary_data; char flags; }; /* included in OpenPACE, but not propagated */ extern BUF_MEM *BUF_MEM_create(size_t len); extern BUF_MEM *BUF_MEM_create_init(const void *buf, size_t len); 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 eac_sm_decrypt(sc_card_t *card, const struct iso_sm_ctx *ctx, const u8 *enc, size_t enclen, u8 **data); 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 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 eac_sm_pre_transmit(sc_card_t *card, const struct iso_sm_ctx *ctx, sc_apdu_t *apdu); static int eac_sm_post_transmit(sc_card_t *card, const struct iso_sm_ctx *ctx, sc_apdu_t *sm_apdu); static int eac_sm_finish(sc_card_t *card, const struct iso_sm_ctx *ctx, sc_apdu_t *apdu); static void eac_sm_clear_free(const struct iso_sm_ctx *ctx); static struct eac_sm_ctx * eac_sm_ctx_create(EAC_CTX *ctx, const unsigned char *id_icc, size_t id_icc_length) { struct eac_sm_ctx *out = malloc(sizeof *out); if (!out) goto err; out->ctx = ctx; if (id_icc && id_icc_length) { out->id_icc = BUF_MEM_create_init(id_icc, id_icc_length); if (!out->id_icc) goto err; } else out->id_icc = NULL; out->eph_pub_key = NULL; out->auxiliary_data = NULL; out->flags = eac_default_flags; if (out->flags & EAC_FLAG_DISABLE_CHECK_TA) TA_disable_checks(out->ctx); if (out->flags & EAC_FLAG_DISABLE_CHECK_CA) CA_disable_passive_authentication(out->ctx); return out; err: free(out); return NULL; } static int eac_sm_start(sc_card_t *card, EAC_CTX *eac_ctx, const unsigned char *id_icc, size_t id_icc_length) { int r; struct iso_sm_ctx *sctx = NULL; if (!eac_ctx || !eac_ctx->key_ctx) { r = SC_ERROR_INVALID_ARGUMENTS; goto err; } sctx = iso_sm_ctx_create(); if (!sctx) { r = SC_ERROR_OUT_OF_MEMORY; goto err; } 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 = 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); r = iso_sm_start(card, sctx); err: if (r < 0) iso_sm_ctx_clear_free(sctx); return r; } static int get_ef_card_access(sc_card_t *card, u8 **ef_cardaccess, size_t *length_ef_cardaccess) { return iso7816_read_binary_sfid(card, SFID_EF_CARDACCESS, ef_cardaccess, length_ef_cardaccess); } /* * 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) { 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; } if (protocol) { 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); } 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); 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 (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; } } p = realloc(*cdata, data_len + encoded_chat_len); if (!p) { r = SC_ERROR_OUT_OF_MEMORY; goto err; } memcpy(p, data, data_len); memcpy(p+data_len, encoded_chat, encoded_chat_len); *cdata = p; r = data_len + encoded_chat_len; sc_debug_hex(ctx, SC_LOG_DEBUG_SM, "MSE command data", p, r); err: free(data); if (encoded_chat) OPENSSL_free(encoded_chat); return r; } 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, 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) { sc_apdu_t apdu; unsigned char *d = NULL; int r; if (!card) { r = SC_ERROR_INVALID_ARGUMENTS; goto err; } 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; sc_format_apdu_ex(&apdu, 0x00, ISO_MSE, p1, p2, d, r, NULL, 0); r = sc_transmit_apdu(card, &apdu); if (r < 0) goto err; if (apdu.resplen) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "MSE:Set AT response data should be empty " "(contains %"SC_FORMAT_LEN_SIZE_T"u bytes)", apdu.resplen); r = SC_ERROR_UNKNOWN_DATA_RECEIVED; goto err; } if (sw1) *sw1 = apdu.sw1; if (sw2) *sw2 = apdu.sw2; err: free(d); return r; } 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 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 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 = 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) { tries = *sw2 & 0x0f; 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, eac_secret_name(secret_key), tries ? "resumed" : "unblocked"); } r = SC_SUCCESS; } else { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Unknown status bytes: SW1=%02X, SW2=%02X\n", *sw1, *sw2); r = SC_ERROR_CARD_CMD_FAILED; } } else if (*sw1 == 0x62 && *sw2 == 0x83) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Password is deactivated\n"); r = SC_ERROR_AUTH_METHOD_BLOCKED; } else { 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 = 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 eac_gen_auth_1_encrypted_nonce(sc_card_t *card, u8 **enc_nonce, size_t *enc_nonce_len) { sc_apdu_t apdu; unsigned char *d = NULL; int r; unsigned char resp[SC_MAX_APDU_RESP_SIZE]; r = encode_gen_auth_cdata(card->ctx, NULL, 0, NULL, 0, NULL, 0, NULL, 0, &d); if (r < 0) goto err; sc_format_apdu_ex(&apdu, 0x00, ISO_GENERAL_AUTHENTICATE, 0x00, 0x00, d, r, resp, sizeof resp); apdu.cla = ISO_COMMAND_CHAINING; r = sc_transmit_apdu(card, &apdu); if (r < 0) goto err; r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r < 0) goto err; 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: free(d); return r; } 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; unsigned char *d = NULL; int r; unsigned char resp[SC_MAX_APDU_RESP_SIZE]; r = encode_gen_auth_cdata(card->ctx, NULL, 0, in, in_len, NULL, 0, NULL, 0, &d); if (r < 0) goto err; sc_format_apdu_ex(&apdu, 0x00, ISO_GENERAL_AUTHENTICATE, 0x00, 0x00, d, r, resp, sizeof resp); apdu.cla = ISO_COMMAND_CHAINING; r = sc_transmit_apdu(card, &apdu); if (r < 0) goto err; r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r < 0) goto err; 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: free(d); return r; } 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; unsigned char *d = NULL; int r; unsigned char resp[SC_MAX_APDU_RESP_SIZE]; r = encode_gen_auth_cdata(card->ctx, NULL, 0, NULL, 0, in, in_len, NULL, 0, &d); if (r < 0) goto err; sc_format_apdu_ex(&apdu, 0x00, ISO_GENERAL_AUTHENTICATE, 0x00, 0x00, d, r, resp, sizeof resp); apdu.cla = ISO_COMMAND_CHAINING; r = sc_transmit_apdu(card, &apdu); if (r < 0) goto err; r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r < 0) goto err; 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: free(d); return r; } 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_out, size_t *recent_car_len, u8 **prev_car_out, size_t *prev_car_len) { sc_apdu_t apdu; unsigned char *d = NULL; int r; unsigned char resp[SC_MAX_APDU_RESP_SIZE]; r = encode_gen_auth_cdata(card->ctx, NULL, 0, NULL, 0, NULL, 0, in, in_len, &d); if (r < 0) goto err; sc_format_apdu_ex(&apdu, 0x00, ISO_GENERAL_AUTHENTICATE, 0x00, 0x00, d, r, resp, sizeof resp); r = sc_transmit_apdu(card, &apdu); if (r < 0) goto err; r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r < 0) goto err; 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: free(d); return r; } static PACE_SEC * get_psec(sc_card_t *card, const char *pin, size_t length_pin, enum s_type pin_id) { char *p = NULL; PACE_SEC *r; 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: ", eac_secret_name(pin_id))) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not create password prompt.\n"); return NULL; } p = malloc(EAC_MAX_MRZ_LEN+1); if (!p) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Not enough memory for %s.\n", eac_secret_name(pin_id)); return NULL; } 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", eac_secret_name(pin_id)); return NULL; } length_pin = strlen(p); if (length_pin > EAC_MAX_MRZ_LEN) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "MRZ too long"); return NULL; } pin = p; } r = PACE_SEC_new(pin, length_pin, pin_id); if (p) { OPENSSL_cleanse(p, length_pin); free(p); } return r; } int perform_pace(sc_card_t *card, struct establish_pace_channel_input pace_input, struct establish_pace_channel_output *pace_output, enum eac_tr_version tr_version) { u8 *p = NULL; EAC_CTX *eac_ctx = NULL; BUF_MEM *enc_nonce = NULL, *mdata = NULL, *mdata_opp = NULL, *token_opp = NULL, *token = NULL, *pub = NULL, *pub_opp = NULL, *comp_pub = NULL, *comp_pub_opp = NULL; PACE_SEC *sec = NULL; CVC_CHAT *chat = NULL; BIO *bio_stdout = NULL; CVC_CERTIFICATE_DESCRIPTION *desc = NULL; int r; const unsigned char *pp; if (!card || !card->reader || !card->reader->ops || !pace_output) return SC_ERROR_INVALID_ARGUMENTS; /* show description in advance to give the user more time to read it... * This behaviour differs from TR-03119 v1.1 p. 44. */ if (pace_input.certificate_description_length && pace_input.certificate_description) { 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."); r = SC_ERROR_INTERNAL; goto err; } 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."); r = SC_ERROR_INTERNAL; goto err; } } 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."); r = SC_ERROR_INTERNAL; goto err; break; case 1: /* text format */ break; case 2: sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Certificate description in " "HTML format can not (yet) be handled."); r = SC_ERROR_NOT_SUPPORTED; goto err; break; case 3: sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Certificate description in " "PDF format can not (yet) be handled."); r = SC_ERROR_NOT_SUPPORTED; goto err; break; default: sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Certificate description in " "unknown format can not be handled."); r = SC_ERROR_NOT_SUPPORTED; goto err; break; } } /* show chat in advance to give the user more time to read it... * This behaviour differs from TR-03119 v1.1 p. 44. */ if (pace_input.chat_length && pace_input.chat) { 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."); r = SC_ERROR_INTERNAL; goto err; } } 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)."); 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)."); r = SC_ERROR_INTERNAL; goto err; } } if (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); if (r < 0) goto err; } else { if (!pace_output->ef_cardaccess_length || !pace_output->ef_cardaccess) { r = get_ef_card_access(card, &pace_output->ef_cardaccess, &pace_output->ef_cardaccess_length); if (r < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not get EF.CardAccess."); goto err; } } sc_debug_hex(card->ctx, SC_LOG_DEBUG_SM, "EF.CardAccess", pace_output->ef_cardaccess, pace_output->ef_cardaccess_length); 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."); r = SC_ERROR_INTERNAL; goto err; } eac_ctx->tr_version = tr_version; 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 properties " "(MSE: Set AT failed)."); goto err; } enc_nonce = BUF_MEM_new(); if (!enc_nonce) { sc_log_openssl(card->ctx); r = SC_ERROR_OUT_OF_MEMORY; goto err; } 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_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."); 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."); r = SC_ERROR_INTERNAL; goto err; } 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."); r = SC_ERROR_INTERNAL; goto err; } 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_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."); r = SC_ERROR_INTERNAL; goto err; } 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."); r = SC_ERROR_INTERNAL; goto err; } 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_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."); 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."); r = SC_ERROR_INTERNAL; goto err; } 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 " "(General Authenticate step 4 failed)."); goto err; } 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."); 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."); r = SC_ERROR_INTERNAL; goto err; } /* Identifier for ICC and PCD */ 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."); r = SC_ERROR_INTERNAL; goto err; } if (comp_pub_opp->length == 0) { r = SC_ERROR_INTERNAL; goto err; } p = realloc(pace_output->id_icc, comp_pub_opp->length); if (!p) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Not enough memory for ID ICC.\n"); r = SC_ERROR_OUT_OF_MEMORY; goto err; } pace_output->id_icc = p; pace_output->id_icc_length = comp_pub_opp->length; memcpy(pace_output->id_icc, comp_pub_opp->data, comp_pub_opp->length); 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"); r = SC_ERROR_OUT_OF_MEMORY; goto err; } pace_output->id_pcd = p; pace_output->id_pcd_length = comp_pub->length; memcpy(pace_output->id_pcd, comp_pub->data, comp_pub->length); sc_debug_hex(card->ctx, SC_LOG_DEBUG_SM, "ID PCD", pace_output->id_pcd, pace_output->id_pcd_length); r = eac_sm_start(card, eac_ctx, pace_output->id_icc, pace_output->id_icc_length); } err: if (enc_nonce) BUF_MEM_free(enc_nonce); if (mdata) BUF_MEM_free(mdata); if (mdata_opp) BUF_MEM_free(mdata_opp); if (token_opp) BUF_MEM_free(token_opp); if (token) BUF_MEM_free(token); if (pub) BUF_MEM_free(pub); if (pub_opp) BUF_MEM_free(pub_opp); if (comp_pub_opp) BUF_MEM_free(comp_pub_opp); if (comp_pub) BUF_MEM_free(comp_pub); PACE_SEC_clear_free(sec); if (bio_stdout) BIO_free_all(bio_stdout); if (desc) CVC_CERTIFICATE_DESCRIPTION_free(desc); if (chat) CVC_CHAT_free(chat); if (r < 0) EAC_CTX_clear_free(eac_ctx); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_SM, r); } 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 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 eac_mse_set_dst(sc_card_t *card, const unsigned char *chr, size_t chr_len) { return eac_mse(card, 0x81, 0xb6, 0, chr, chr_len, NULL, 0, NULL, 0, NULL, 0, NULL, NULL, NULL); } static int eac_get_challenge(sc_card_t *card, unsigned char *challenge, size_t len) { sc_apdu_t apdu; int r; if (!card) { r = SC_ERROR_INVALID_ARGUMENTS; goto err; } sc_format_apdu_ex(&apdu, 0x00, 0x84, 0x00, 0x00, NULL, 0, challenge, len); r = sc_transmit_apdu(card, &apdu); if (r < 0) goto err; r = sc_check_sw(card, apdu.sw1, apdu.sw2); err: return r; } static int eac_verify(sc_card_t *card, const unsigned char *cert, size_t cert_len) { sc_apdu_t apdu; int r, class, tag; long int length; memset(&apdu, 0, sizeof apdu); if (!card) { r = SC_ERROR_INVALID_ARGUMENTS; goto err; } 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"); r = SC_ERROR_INTERNAL; goto err; } sc_format_apdu_ex(&apdu, 0x00, 0x2A, 0x00, 0xbe, (unsigned char *) cert, length, NULL, 0); r = sc_transmit_apdu(card, &apdu); if (r < 0) goto err; r = sc_check_sw(card, apdu.sw1, apdu.sw2); err: return r; } static int eac_external_authenticate(sc_card_t *card, unsigned char *signature, size_t signature_len) { int r; sc_apdu_t apdu; memset(&apdu, 0, sizeof apdu); if (!card) { r = SC_ERROR_INVALID_ARGUMENTS; goto err; } sc_format_apdu_ex(&apdu, 0x00, 0x82, 0x00, 0x00, signature, signature_len, NULL, 0); r = sc_transmit_apdu(card, &apdu); if (r < 0) goto err; r = sc_check_sw(card, apdu.sw1, apdu.sw2); err: 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, const unsigned char *privkey, size_t privkey_len, const unsigned char *auxiliary_data, size_t auxiliary_data_len) { int r; const unsigned char *cert = NULL; size_t cert_len = 0, ef_cardaccess_length = 0; CVC_CERT *cvc_cert = NULL; BUF_MEM *nonce = NULL, *signature = NULL; struct iso_sm_ctx *isosmctx = 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; goto err; } 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; goto err; } isosmctx = card->sm_ctx.info.cmd_data; if (!isosmctx->priv_data) { r = get_ef_card_access(card, &ef_cardaccess, &ef_cardaccess_length); if (r < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not get EF.CardAccess."); goto err; } sc_debug_hex(card->ctx, SC_LOG_DEBUG_SM, "EF.CardAccess", ef_cardaccess, ef_cardaccess_length); 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."); r = SC_ERROR_INTERNAL; goto err; } 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; while (*certs && *certs_lens) { cert = *certs; cert_len = *certs_lens; 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) { sc_log_openssl(card->ctx); r = SC_ERROR_INVALID_DATA; goto err; } cert = *certs; r = eac_mse_set_dst(card, cvc_cert->body->certificate_authority_reference->data, cvc_cert->body->certificate_authority_reference->length); if (r < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not select protocol properties " "(MSE: Set AT failed)."); goto err; } r = eac_verify(card, cert, cert_len); if (r < 0) goto err; chr = cvc_cert->body->certificate_holder_reference->data; chr_len = cvc_cert->body->certificate_holder_reference->length; 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."); r = SC_ERROR_INTERNAL; goto err; } if (eacsmctx->eph_pub_key) 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."); r = SC_ERROR_INTERNAL; goto err; } 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 properties " "(MSE: Set AT failed)."); goto err; } nonce = BUF_MEM_create(TA_NONCE_LENGTH); if (!nonce) { sc_log_openssl(card->ctx); r = SC_ERROR_INTERNAL; goto err; } 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."); r = SC_ERROR_INTERNAL; goto err; } if (eacsmctx->auxiliary_data) 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."); r = SC_ERROR_INTERNAL; goto err; } r = eac_external_authenticate(card, (unsigned char *) signature->data, signature->length); err: if (cvc_cert) CVC_CERT_free(cvc_cert); free(ef_cardaccess); EAC_CTX_clear_free(eac_ctx); BUF_MEM_clear_free(nonce); BUF_MEM_clear_free(signature); if (card) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_SM, r); else return r; } static int eac_mse_set_at_ca(sc_card_t *card, int protocol) { return eac_mse_set_at(card, 0x41, protocol, NULL, 0, NULL, 0, NULL, 0, NULL, 0, NULL, NULL, NULL); } 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; unsigned char *d = NULL, *p, *q; int r; unsigned char resp[SC_MAX_APDU_RESP_SIZE]; BUF_MEM *nonce_out = NULL, *token_out = NULL; if (!eph_pub_key) { r = SC_ERROR_INVALID_ARGUMENTS; goto err; } 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); r = sc_transmit_apdu(card, &apdu); if (r < 0) goto err; r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r < 0) goto err; nonce_out = BUF_MEM_new(); token_out = BUF_MEM_new(); if (!nonce_out || !token_out) { r = SC_ERROR_NOT_ENOUGH_MEMORY; goto err; } 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); if (*token) BUF_MEM_free(*token); *nonce = nonce_out; *token = token_out; nonce_out = NULL; token_out = NULL; err: BUF_MEM_free(nonce_out); BUF_MEM_free(token_out); free(d); return r; } static int get_ef_card_security(sc_card_t *card, u8 **ef_security, size_t *length_ef_security) { return iso7816_read_binary_sfid(card, SFID_EF_CARDSECURITY, ef_security, length_ef_security); } int perform_chip_authentication(sc_card_t *card, unsigned char **ef_cardsecurity, size_t *ef_cardsecurity_len) { int r; BUF_MEM *picc_pubkey = NULL; struct iso_sm_ctx *isosmctx; struct eac_sm_ctx *eacsmctx; if (!card || !ef_cardsecurity || !ef_cardsecurity_len) { r = SC_ERROR_INVALID_ARGUMENTS; goto err; } isosmctx = card->sm_ctx.info.cmd_data; if (!isosmctx->priv_data) { r = SC_ERROR_INVALID_ARGUMENTS; goto err; } eacsmctx = isosmctx->priv_data; /* Passive Authentication */ if (!*ef_cardsecurity && !*ef_cardsecurity_len) { r = get_ef_card_security(card, ef_cardsecurity, ef_cardsecurity_len); if (r < 0 || !ef_cardsecurity || !ef_cardsecurity_len) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not get EF.CardSecurity."); goto err; } } 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."); r = SC_ERROR_INTERNAL; goto err; } r = perform_chip_authentication_ex(card, eacsmctx->ctx, (unsigned char *) picc_pubkey->data, picc_pubkey->length); err: BUF_MEM_clear_free(picc_pubkey); if (card) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_SM, r); else return r; } int perform_chip_authentication_ex(sc_card_t *card, void *eac_ctx, unsigned char *picc_pubkey, size_t picc_pubkey_len) { int r; BUF_MEM *picc_pubkey_buf = NULL, *nonce = NULL, *token = NULL, *eph_pub_key = NULL; EAC_CTX *ctx = eac_ctx; if (!card || !ctx) { r = SC_ERROR_INVALID_ARGUMENTS; goto err; } 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."); r = SC_ERROR_INTERNAL; goto err; } 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 properties " "(MSE: Set AT failed)."); goto err; } 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."); r = SC_ERROR_INTERNAL; goto err; } 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; } 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."); 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."); r = SC_ERROR_INTERNAL; goto err; } /* 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."); r = SC_ERROR_INTERNAL; goto err; } if (card->sm_ctx.sm_mode != SM_MODE_TRANSMIT) { r = eac_sm_start(card, ctx, NULL, 0); } err: BUF_MEM_clear_free(picc_pubkey_buf); BUF_MEM_clear_free(nonce); BUF_MEM_clear_free(token); BUF_MEM_clear_free(eph_pub_key); if (card) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_SM, r); else return r; } static int increment_ssc(struct eac_sm_ctx *eacsmctx) { if (!eacsmctx) return SC_ERROR_INVALID_ARGUMENTS; if (!EAC_increment_ssc(eacsmctx->ctx)) return SC_ERROR_INTERNAL; return SC_SUCCESS; } static int 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 eac_sm_ctx *eacsmctx; if (!card || !ctx || !enc || !ctx->priv_data) { r = SC_ERROR_INVALID_ARGUMENTS; goto err; } eacsmctx = ctx->priv_data; databuf = BUF_MEM_create_init(data, datalen); encbuf = EAC_encrypt(eacsmctx->ctx, databuf); if (!databuf || !encbuf || !encbuf->length) { sc_log_openssl(card->ctx); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not encrypt data."); r = SC_ERROR_INTERNAL; goto err; } p = realloc(*enc, encbuf->length); if (!p) { r = SC_ERROR_OUT_OF_MEMORY; goto err; } *enc = p; memcpy(*enc, encbuf->data, encbuf->length); r = encbuf->length; err: BUF_MEM_clear_free(databuf); if (encbuf) BUF_MEM_free(encbuf); return r; } static int 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 eac_sm_ctx *eacsmctx; if (!card || !ctx || !enc || !ctx->priv_data || !data) { r = SC_ERROR_INVALID_ARGUMENTS; goto err; } eacsmctx = ctx->priv_data; encbuf = BUF_MEM_create_init(enc, enclen); databuf = EAC_decrypt(eacsmctx->ctx, encbuf); if (!encbuf || !databuf || !databuf->length) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not decrypt data."); sc_log_openssl(card->ctx); r = SC_ERROR_INTERNAL; goto err; } p = realloc(*data, databuf->length); if (!p) { r = SC_ERROR_OUT_OF_MEMORY; goto err; } *data = p; memcpy(*data, databuf->data, databuf->length); r = databuf->length; err: BUF_MEM_clear_free(databuf); if (encbuf) BUF_MEM_free(encbuf); return r; } static int 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 eac_sm_ctx *eacsmctx; if (!card || !ctx || !ctx->priv_data || !macdata) { r = SC_ERROR_INVALID_ARGUMENTS; goto err; } eacsmctx = ctx->priv_data; inbuf = BUF_MEM_create_init(data, datalen); if (!inbuf) { r = SC_ERROR_OUT_OF_MEMORY; goto err; } macbuf = EAC_authenticate(eacsmctx->ctx, inbuf); if (!macbuf || !macbuf->length) { sc_log_openssl(card->ctx); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not compute message authentication code (MAC)."); r = SC_ERROR_INTERNAL; goto err; } p = realloc(*macdata, macbuf->length); if (!p) { r = SC_ERROR_OUT_OF_MEMORY; goto err; } *macdata = p; memcpy(*macdata, macbuf->data, macbuf->length); r = macbuf->length; err: if (inbuf) BUF_MEM_free(inbuf); if (macbuf) BUF_MEM_free(macbuf); return r; } 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) { int r; BUF_MEM *inbuf = NULL, *my_mac = NULL; struct eac_sm_ctx *eacsmctx; if (!card || !ctx || !ctx->priv_data) { r = SC_ERROR_INVALID_ARGUMENTS; goto err; } eacsmctx = ctx->priv_data; inbuf = BUF_MEM_create_init(macdata, macdatalen); if (!inbuf) { r = SC_ERROR_OUT_OF_MEMORY; goto err; } 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."); r = SC_ERROR_INTERNAL; goto err; } if (my_mac->length != maclen || memcmp(my_mac->data, mac, maclen) != 0) { r = SC_ERROR_OBJECT_NOT_VALID; sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Authentication data not verified"); goto err; } sc_debug(card->ctx, SC_LOG_DEBUG_SM, "Authentication data verified"); r = SC_SUCCESS; err: if (inbuf) BUF_MEM_free(inbuf); if (my_mac) BUF_MEM_free(my_mac); return r; } static int 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; BUF_MEM *signature = NULL; unsigned char *sequence = NULL; if (!card) return SC_ERROR_INVALID_ARGUMENTS; if(!ctx || !apdu || !ctx->priv_data) { r = SC_ERROR_INVALID_ARGUMENTS; goto err; } r = increment_ssc(ctx->priv_data); err: if (cvc_cert) CVC_CERT_free(cvc_cert); if (signature) BUF_MEM_free(signature); if (cert) OPENSSL_free(cert); if (sequence) OPENSSL_free(sequence); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_SM, r); } static int 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_SM, increment_ssc(ctx->priv_data)); } static int eac_sm_finish(sc_card_t *card, const struct iso_sm_ctx *ctx, sc_apdu_t *apdu) { if (!card) return SC_ERROR_INVALID_ARGUMENTS; if(!ctx || !ctx->priv_data || !apdu) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_SM, SC_ERROR_INVALID_ARGUMENTS); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_SM, SC_SUCCESS); } static void eac_sm_clear_free(const struct iso_sm_ctx *ctx) { if (ctx) { 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); } } } #else int perform_pace(sc_card_t *card, struct establish_pace_channel_input pace_input, struct establish_pace_channel_output *pace_output, enum eac_tr_version tr_version) { int r; 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); } else { r = SC_ERROR_NOT_SUPPORTED; } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_SM, r); } int perform_terminal_authentication(sc_card_t *card, const unsigned char **certs, const size_t *certs_lens, const unsigned char *privkey, size_t privkey_len, const unsigned char *auxiliary_data, size_t auxiliary_data_len) { 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) { 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) { SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_SM, SC_ERROR_NOT_SUPPORTED); } #endif static const char *MRZ_name = "MRZ"; 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 *eac_secret_name(enum s_type pin_id) { switch (pin_id) { case PACE_MRZ: return MRZ_name; case PACE_PUK: return PUK_name; case PACE_PIN: return PIN_name; case PACE_CAN: return CAN_name; default: return UNDEF_name; } } int eac_pace_get_tries_left(sc_card_t *card, enum s_type pin_id, int *tries_left) { int r; u8 sw1, sw2; if (tries_left) { #if defined(ENABLE_OPENPACE) && defined(ENABLE_SM) r = eac_mse_set_at_pace(card, 0, pin_id, 0, &sw1, &sw2); #else sc_apdu_t apdu; 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; #endif if (r > 0 && (sw1 == 0x63) && ((sw2 & 0xc0) == 0xc0)) { *tries_left = sw2 & 0x0f; } else { *tries_left = -1; } } else { r = SC_ERROR_INVALID_ARGUMENTS; } return r; } OpenSC-0.26.1/src/sm/sm-eac.h000066400000000000000000000160741474147347300155010ustar00rootroot00000000000000/* * Copyright (C) 2011-2015 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 */ /** * @file * @defgroup eac Interface to Extended Access Control * @{ */ #ifndef _SC_EAC_H #define _SC_EAC_H #include "libopensc/opensc.h" #include "libopensc/pace.h" #include "sm/sm-iso.h" #ifdef __cplusplus extern "C" { #endif #ifdef ENABLE_OPENPACE #include #include #include /** @brief ASN.1 type for authenticated auxiliary data for terminal authentication */ typedef STACK_OF(CVC_DISCRETIONARY_DATA_TEMPLATE) ASN1_AUXILIARY_DATA; DECLARE_ASN1_FUNCTIONS(ASN1_AUXILIARY_DATA) #else /** @brief Type of the secret */ enum s_type { /** @brief MRZ is the Machine Readable Zone, printed on the card, encoding * the personal information of the user */ PACE_MRZ = 1, /** @brief CAN is the Card access number printed on the card */ PACE_CAN, /** @brief PIN is the Personal Identification Number, a secret known only * to the user and not printed on the card */ PACE_PIN, /** @brief PUK is the Personal Unblocking key. This type of secret is used * when the card is suspended due to too many incorrect PACE runs */ PACE_PUK, /** @brief This type of secret is not defined in BSI TR-03110. We use it as * a generic type, so we can use PACE independent from a ID card */ PACE_RAW, /** @brief Undefined type, if nothing else matches */ PACE_SEC_UNDEF }; /** * @brief Identification of the specifications to use. * * @note TR-03110 v2.01 differs from all later versions of the Technical * Guideline in how the authentication token is calculated. Therefore old test * cards are incompatible with the newer specification. */ enum eac_tr_version { /** @brief Undefined type, if nothing else matches */ EAC_TR_VERSION = 0, /** @brief Perform EAC according to TR-03110 v2.01 */ EAC_TR_VERSION_2_01, /** @brief Perform EAC according to TR-03110 v2.02 and later */ EAC_TR_VERSION_2_02, }; #endif /** @brief File identifier of EF.CardAccess */ #define FID_EF_CARDACCESS 0x011C /** @brief Short file identifier of EF.CardAccess */ #define SFID_EF_CARDACCESS 0x1C /** @brief File identifier of EF.CardSecurity */ #define FID_EF_CARDSECURITY 0x011D /** @brief Short file identifier of EF.CardAccess */ #define SFID_EF_CARDSECURITY 0x1D /** @brief Maximum length of PIN */ #define EAC_MAX_PIN_LEN 6 /** @brief Minimum length of PIN */ #define EAC_MIN_PIN_LEN 6 /** @brief Length of CAN */ #define EAC_CAN_LEN 6 /** @brief Minimum length of MRZ */ #define EAC_MAX_MRZ_LEN 128 /** @brief Number of retries for PIN */ #define EAC_MAX_PIN_TRIES 3 /** @brief Usage counter of PIN in suspended state */ #define EAC_UC_PIN_SUSPENDED 1 /** * @brief Names the type of the PACE secret * * @param pin_id type of the PACE secret * * @return Printable string containing the name */ const char *eac_secret_name(enum s_type pin_id); /** * @brief Establish secure messaging using PACE * * Modifies \a card to use the ISO SM driver and initializes the data * structures to use the established SM channel. * * Prints certificate description and card holder authorization template if * given in a human readable form to stdout. If no secret is given, the user is * asked for it. Only \a pace_input.pin_id is mandatory, the other members of * \a pace_input can be set to \c 0 or \c NULL respectively. * * The buffers in \a pace_output are allocated using \c realloc() and should be * set to NULL, if empty. If an EF.CardAccess is already present, this file is * reused and not fetched from the card. * * @param[in,out] card * @param[in] pace_input * @param[in,out] pace_output * @param[in] tr_version Version of TR-03110 to use with PACE * * @return \c SC_SUCCESS or error code if an error occurred */ int perform_pace(sc_card_t *card, struct establish_pace_channel_input pace_input, struct establish_pace_channel_output *pace_output, enum eac_tr_version tr_version); /** * @brief Terminal Authentication version 2 * * @param[in] card * @param[in] certs chain of cv certificates, the last certificate * is the terminal's certificate, array should be * terminated with \c NULL * @param[in] certs_lens length of each element in \c certs, should be * terminated with \c 0 * @param[in] privkey The terminal's private key * @param[in] privkey_len length of \a privkey * @param[in] auxiliary_data auxiliary data for age/validity/community ID * verification. Should be an ASN1 object tagged * with \c 0x67 * @param[in] auxiliary_data_len length of \a auxiliary_data * * @return \c SC_SUCCESS or error code if an error occurred */ int perform_terminal_authentication(sc_card_t *card, const unsigned char **certs, const size_t *certs_lens, const unsigned char *privkey, size_t privkey_len, const unsigned char *auxiliary_data, size_t auxiliary_data_len); /** * @brief Establish secure messaging using Chip Authentication version 2 * * Switches the SM context of \c card to the new established keys. * * @param[in] card * @param[in,out] ef_cardsecurity * @param[in,out] ef_cardsecurity_len * * @return \c SC_SUCCESS or error code if an error occurred */ int perform_chip_authentication(sc_card_t *card, unsigned char **ef_cardsecurity, size_t *ef_cardsecurity_len); int perform_chip_authentication_ex(sc_card_t *card, void *eacsmctx, unsigned char *picc_pubkey, size_t picc_pubkey_len); /** * @brief Sends an MSE:Set AT to determine the number of remaining tries * * @param[in] card * @param[in] pin_id Type of secret (usually PIN or CAN). You may use enum s_type from \c . * @param[in,out] tries_left Tries left or -1 if no specific number has been returned by the card (e.g. when there is no limit in retries). * * @return \c SC_SUCCESS or error code if an error occurred */ int eac_pace_get_tries_left(sc_card_t *card, enum s_type pin_id, int *tries_left); /** @brief Disable checking validity period of CV certificates */ #define EAC_FLAG_DISABLE_CHECK_TA 2 /** @brief Disable checking passive authentication during CA */ #define EAC_FLAG_DISABLE_CHECK_CA 4 /** @brief Use \c eac_default_flags to disable checks for EAC/SM */ extern char eac_default_flags; #ifdef __cplusplus } #endif #endif /* @} */ OpenSC-0.26.1/src/sm/sm-iso-internal.h000066400000000000000000000055071474147347300173540ustar00rootroot00000000000000/* * Copyright (C) 2012-2015 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 */ /** * @file * @defgroup sm Interface to Secure Messaging (SM) defined in ISO 7816 * @{ */ #ifndef _ISO_SM_INTERNAL_H #define _ISO_SM_INTERNAL_H #include "libopensc/opensc.h" #ifdef __cplusplus extern "C" { #endif /* @brief Protect an APDU with Secure Messaging * * If secure messaging (SM) is activated in \a sctx and \a apdu is not already * SM protected, \a apdu is processed with the following steps: * \li call to \a sctx->pre_transmit * \li encrypt \a apdu calling \a sctx->encrypt * \li authenticate \a apdu calling \a sctx->authenticate * \li copy the SM protected data to \a sm_apdu * * Data for authentication or encryption is always padded before the callback * functions are called * * @param[in] card * @param[in] apdu * @param[in,out] sm_apdu * * @return \c SC_SUCCESS or error code if an error occurred */ int iso_get_sm_apdu(struct sc_card *card, struct sc_apdu *apdu, struct sc_apdu **sm_apdu); /* @brief Remove Secure Messaging from an APDU * * If secure messaging (SM) is activated in \a sctx and \a apdu is not already * SM protected, \a apdu is processed with the following steps: * \li verify SM protected \a apdu calling \a sctx->verify_authentication * \li decrypt SM protected \a apdu calling \a sctx->decrypt * \li copy decrypted/authenticated data and status bytes to \a apdu * * Callback functions must not remove padding. * * @param[in] card * @param[in,out] apdu * @param[in,out] sm_apdu will be freed when done. * * @return \c SC_SUCCESS or error code if an error occurred */ int iso_free_sm_apdu(struct sc_card *card, struct sc_apdu *apdu, struct sc_apdu **sm_apdu); /** * @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 * sc_disconnect_card() . * * @param[in] card * * @return \c SC_SUCCESS or error code if an error occurred */ int iso_sm_close(struct sc_card *card); #ifdef __cplusplus } #endif #endif /* @} */ OpenSC-0.26.1/src/sm/sm-iso.c000066400000000000000000000473341474147347300155410ustar00rootroot00000000000000/* * Copyright (C) 2011-2015 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 "sm-iso-internal.h" #include "libopensc/asn1.h" #include "libopensc/log.h" #include "sm/sm-iso.h" #include #include #ifdef ENABLE_SM static const struct sc_asn1_entry c_sm_capdu[] = { { "Cryptogram", SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x05, SC_ASN1_OPTIONAL, NULL, NULL }, { "Padding-content indicator followed by cryptogram", SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x07, SC_ASN1_OPTIONAL, NULL, NULL }, { "Protected Le", SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x17, SC_ASN1_OPTIONAL, NULL, NULL }, { "Cryptographic Checksum", SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x0E, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL , 0 , 0 , 0 , NULL , NULL } }; static const struct sc_asn1_entry c_sm_rapdu[] = { { "Cryptogram", SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x05, SC_ASN1_OPTIONAL, NULL, NULL }, { "Padding-content indicator followed by cryptogram" , SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x07, SC_ASN1_OPTIONAL, NULL, NULL }, { "Processing Status", SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x19, 0 , NULL, NULL }, { "Cryptographic Checksum", SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x0E, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; static int add_iso_pad(const u8 *data, size_t datalen, size_t block_size, u8 **padded) { u8 *p; int p_len; if (!padded) return SC_ERROR_INVALID_ARGUMENTS; /* calculate length of padded message */ 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) memcpy(p, data, datalen); *padded = p; /* now add iso padding */ memset(p + datalen, 0x80, 1); memset(p + datalen + 1, 0, p_len - datalen - 1); return p_len; } static int add_padding(const struct iso_sm_ctx *ctx, const u8 *data, size_t datalen, u8 **padded) { u8 *p; switch (ctx->padding_indicator) { case SM_NO_PADDING: if (*padded != data) { 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 (int)datalen; case SM_ISO_PADDING: return add_iso_pad(data, datalen, ctx->block_length, padded); default: return SC_ERROR_INVALID_ARGUMENTS; } } static int rm_padding(u8 padding_indicator, const u8 *data, size_t datalen) { size_t len; if (!datalen || !data) return SC_ERROR_INVALID_ARGUMENTS; switch (padding_indicator) { case SM_NO_PADDING: len = datalen; break; case SM_ISO_PADDING: len = datalen; while (len) { len--; if (data[len]) break; } if (data[len] != 0x80) return SC_ERROR_INVALID_DATA; break; default: return SC_ERROR_NOT_SUPPORTED; } return (int)len; } static int format_le(size_t le, struct sc_asn1_entry *le_entry, u8 **lebuf, size_t *le_len) { u8 *p; if (!lebuf || !le_len || !*le_len) return SC_ERROR_INVALID_ARGUMENTS; p = realloc(*lebuf, *le_len); if (!p) return SC_ERROR_OUT_OF_MEMORY; *lebuf = p; switch (*le_len) { case 1: p[0] = le & 0xff; break; case 2: p[0] = (le >> 8) & 0xff; p[1] = le & 0xff; break; case 3: p[0] = 0x00; p[1] = (le >> 8) & 0xff; p[2] = le & 0xff; break; default: return SC_ERROR_INVALID_ARGUMENTS; } sc_format_asn1_entry(le_entry, *lebuf, le_len, SC_ASN1_PRESENT); return SC_SUCCESS; } static int prefix_buf(u8 prefix, u8 *buf, size_t buflen, u8 **cat) { u8 *p = NULL; int ptr_same = *cat == buf; p = realloc(*cat, buflen + 1); if (!p) return SC_ERROR_OUT_OF_MEMORY; if (ptr_same) { memmove(p + 1, p, buflen); } else { memcpy(p + 1, buf, buflen); } p[0] = prefix; *cat = p; return (int)buflen + 1; } static int format_data(sc_card_t *card, const struct iso_sm_ctx *ctx, int prepend_padding_indicator, const u8 *data, size_t datalen, struct sc_asn1_entry *formatted_encrypted_data_entry, u8 **formatted_data, size_t *formatted_data_len) { int r; u8 *pad_data = NULL; size_t pad_data_len = 0; if (!ctx || !formatted_data || !formatted_data_len) { r = SC_ERROR_INVALID_ARGUMENTS; goto err; } r = add_padding(ctx, data, datalen, &pad_data); if (r < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not add padding to data: %s", sc_strerror(r)); goto err; } pad_data_len = r; 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_log_hex(card->ctx, "Cryptogram", *formatted_data, r); if (prepend_padding_indicator) { r = prefix_buf(ctx->padding_indicator, *formatted_data, r, formatted_data); if (r < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not prepend padding indicator to formatted " "data: %s", sc_strerror(r)); goto err; } } *formatted_data_len = r; sc_format_asn1_entry(formatted_encrypted_data_entry, *formatted_data, formatted_data_len, SC_ASN1_PRESENT); r = SC_SUCCESS; err: if (pad_data) { sc_mem_clear(pad_data, pad_data_len); free(pad_data); } return r; } static int format_head(const struct iso_sm_ctx *ctx, const sc_apdu_t *apdu, u8 **formatted_head) { u8 *p; if (!apdu || !formatted_head) return SC_ERROR_INVALID_ARGUMENTS; p = realloc(*formatted_head, 4); if (!p) return SC_ERROR_OUT_OF_MEMORY; p[0] = apdu->cla; p[1] = apdu->ins; p[2] = apdu->p1; p[3] = apdu->p2; *formatted_head = p; return add_padding(ctx, *formatted_head, 4, formatted_head); } static int sm_encrypt(const struct iso_sm_ctx *ctx, sc_card_t *card, const sc_apdu_t *apdu, sc_apdu_t **psm_apdu) { struct sc_asn1_entry sm_capdu[5]; 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; sc_apdu_t *sm_apdu = NULL; if (!apdu || !ctx || !card || !card->reader || !psm_apdu) { r = SC_ERROR_INVALID_ARGUMENTS; goto err; } if ((apdu->cla & 0x0C) == 0x0C) { r = SC_ERROR_INVALID_ARGUMENTS; sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Given APDU is already protected with some secure messaging"); goto err; } sc_copy_asn1_entry(c_sm_capdu, sm_capdu); sm_apdu = malloc(sizeof(sc_apdu_t)); if (!sm_apdu) { r = SC_ERROR_OUT_OF_MEMORY; goto err; } sm_apdu->control = apdu->control; sm_apdu->flags = apdu->flags; sm_apdu->cla = apdu->cla|0x0C; sm_apdu->ins = apdu->ins; sm_apdu->p1 = apdu->p1; sm_apdu->p2 = apdu->p2; r = format_head(ctx, sm_apdu, &mac_data); if (r < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not format header of SM apdu"); goto err; } mac_data_len = r; switch (apdu->cse) { case SC_APDU_CASE_1: break; case SC_APDU_CASE_2_SHORT: le_len = 1; r = format_le(apdu->le, sm_capdu + 2, &le, &le_len); if (r < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not format Le of SM apdu"); goto err; } 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) { /* T0 extended APDUs look just like short APDUs */ le_len = 1; r = format_le(apdu->le, sm_capdu + 2, &le, &le_len); if (r < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not format Le of SM apdu"); goto err; } } else { /* in case of T1 always use 2 bytes for length */ le_len = 2; r = format_le(apdu->le, sm_capdu + 2, &le, &le_len); if (r < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not format Le of SM apdu"); goto err; } } sc_log_hex(card->ctx, "Protected Le (plain)", le, le_len); break; case SC_APDU_CASE_3_SHORT: case SC_APDU_CASE_3_EXT: if (apdu->ins & 1) { r = format_data(card, ctx, 0, apdu->data, apdu->datalen, sm_capdu + 0, &fdata, &fdata_len); } else { r = format_data(card, ctx, 1, apdu->data, apdu->datalen, sm_capdu + 1, &fdata, &fdata_len); } if (r < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not format data of SM apdu"); goto err; } sc_log_hex(card->ctx, "Padding-content indicator followed by cryptogram (plain)", fdata, fdata_len); break; case SC_APDU_CASE_4_SHORT: /* in case of T0 no Le byte is added */ if (card->reader->active_protocol != SC_PROTO_T0) { le_len = 1; r = format_le(apdu->le, sm_capdu + 2, &le, &le_len); if (r < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not format Le of SM apdu"); goto err; } sc_log_hex(card->ctx, "Protected Le (plain)", le, le_len); } if (apdu->ins & 1) { r = format_data(card, ctx, 0, apdu->data, apdu->datalen, sm_capdu + 0, &fdata, &fdata_len); } else { r = format_data(card, ctx, 1, apdu->data, apdu->datalen, sm_capdu + 1, &fdata, &fdata_len); } if (r < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not format data of SM apdu"); goto err; } sc_log_hex(card->ctx, "Padding-content indicator followed by cryptogram (plain)", fdata, fdata_len); break; case SC_APDU_CASE_4_EXT: if (card->reader->active_protocol == SC_PROTO_T0) { /* again a T0 extended case 4 APDU looks just * like a short APDU, the additional data is * transferred using ENVELOPE and GET RESPONSE */ } else { /* only 2 bytes are use to specify the length of the * expected data */ le_len = 2; r = format_le(apdu->le, sm_capdu + 2, &le, &le_len); if (r < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not format Le of SM apdu"); goto err; } sc_log_hex(card->ctx, "Protected Le (plain)", le, le_len); } if (apdu->ins & 1) { r = format_data(card, ctx, 0, apdu->data, apdu->datalen, sm_capdu + 0, &fdata, &fdata_len); } else { r = format_data(card, ctx, 1, apdu->data, apdu->datalen, sm_capdu + 1, &fdata, &fdata_len); } if (r < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not format data of SM apdu"); goto err; } sc_log_hex(card->ctx, "Padding-content indicator followed by cryptogram (plain)", fdata, fdata_len); break; default: sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Unhandled apdu case"); r = SC_ERROR_INVALID_DATA; goto err; } r = sc_asn1_encode(card->ctx, sm_capdu, (u8 **) &asn1, &asn1_len); if (r < 0) { goto err; } if (asn1_len) { p = realloc(mac_data, mac_data_len + asn1_len); if (!p) { r = SC_ERROR_OUT_OF_MEMORY; goto err; } mac_data = p; 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); if (r < 0) { goto err; } mac_data_len = r; } sc_log_hex(card->ctx, "Data to authenticate", mac_data, mac_data_len); r = ctx->authenticate(card, ctx, mac_data, mac_data_len, &mac); if (r < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not get authentication code"); goto err; } mac_len = r; sc_log_hex(card->ctx, "Cryptographic Checksum (plain)", mac, mac_len); /* format SM apdu */ sc_format_asn1_entry(sm_capdu + 3, mac, &mac_len, SC_ASN1_PRESENT); r = sc_asn1_encode(card->ctx, sm_capdu, (u8 **) &sm_data, &sm_data_len); if (r < 0) goto err; sm_apdu->data = sm_data; sm_apdu->datalen = sm_data_len; sm_apdu->lc = sm_data_len; sm_apdu->le = 0; /* 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; 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; 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 = calloc(1, sm_apdu->resplen); if (!resp_data) { r = SC_ERROR_OUT_OF_MEMORY; goto err; } sm_apdu->resp = resp_data; sc_log_hex(card->ctx, "ASN.1 encoded encrypted APDU data", sm_apdu->data, sm_apdu->datalen); *psm_apdu = sm_apdu; err: free(fdata); free(asn1); free(mac_data); free(mac); free(le); if (r < 0) { free(resp_data); free(sm_apdu); free(sm_data); } return r; } static int sm_decrypt(const struct iso_sm_ctx *ctx, sc_card_t *card, const sc_apdu_t *sm_apdu, sc_apdu_t *apdu) { int r; struct sc_asn1_entry sm_rapdu[5]; struct sc_asn1_entry my_sm_rapdu[5]; u8 sw[2], mac[8], fdata[SC_MAX_EXT_APDU_BUFFER_SIZE]; size_t sw_len = sizeof sw, mac_len = sizeof mac, fdata_len = sizeof fdata, buf_len, asn1_len, fdata_offset = 0; const u8 *buf; u8 *data = NULL, *mac_data = NULL, *asn1 = NULL; sc_copy_asn1_entry(c_sm_rapdu, sm_rapdu); sc_format_asn1_entry(sm_rapdu + 0, fdata, &fdata_len, 0); sc_format_asn1_entry(sm_rapdu + 1, fdata, &fdata_len, 0); sc_format_asn1_entry(sm_rapdu + 2, sw, &sw_len, 0); sc_format_asn1_entry(sm_rapdu + 3, mac, &mac_len, 0); r = sc_asn1_decode(card->ctx, sm_rapdu, sm_apdu->resp, sm_apdu->resplen, &buf, &buf_len); if (r < 0) goto err; if (buf_len > 0) { r = SC_ERROR_UNKNOWN_DATA_RECEIVED; goto err; } if (sm_rapdu[3].flags & SC_ASN1_PRESENT) { /* copy from sm_apdu to my_sm_apdu, but leave mac at default */ sc_copy_asn1_entry(sm_rapdu, my_sm_rapdu); sc_copy_asn1_entry(&c_sm_rapdu[3], &my_sm_rapdu[3]); r = sc_asn1_encode(card->ctx, my_sm_rapdu, &asn1, &asn1_len); if (r < 0) goto err; r = add_padding(ctx, asn1, asn1_len, &mac_data); if (r < 0) { goto err; } r = ctx->verify_authentication(card, ctx, mac, mac_len, mac_data, r); if (r < 0) goto err; } else { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Cryptographic Checksum missing"); r = SC_ERROR_ASN1_OBJECT_NOT_FOUND; goto err; } if (sm_rapdu[1].flags & SC_ASN1_PRESENT) { if (ctx->padding_indicator != fdata[0]) { r = SC_ERROR_UNKNOWN_DATA_RECEIVED; goto err; } fdata_offset = 1; } if (sm_rapdu[0].flags & SC_ASN1_PRESENT || sm_rapdu[1].flags & SC_ASN1_PRESENT) { r = ctx->decrypt(card, ctx, fdata + fdata_offset, fdata_len - fdata_offset, &data); if (r < 0) goto err; buf_len = r; r = rm_padding(ctx->padding_indicator, data, buf_len); if (r < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not remove padding"); goto err; } if (apdu->resplen < (size_t) r || (r && !apdu->resp)) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Response of SM APDU %"SC_FORMAT_LEN_SIZE_T"u byte%s too long", r-apdu->resplen, r-apdu->resplen < 2 ? "" : "s"); r = SC_ERROR_OUT_OF_MEMORY; goto err; } memcpy(apdu->resp, data, r); apdu->resplen = r; } else { apdu->resplen = 0; } if (sm_rapdu[2].flags & SC_ASN1_PRESENT) { if (sw_len != 2) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Length of processing status bytes must be 2"); r = SC_ERROR_ASN1_END_OF_CONTENTS; goto err; } apdu->sw1 = sw[0]; apdu->sw2 = sw[1]; } else { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Authenticated status bytes are missing"); r = SC_ERROR_ASN1_OBJECT_NOT_FOUND; goto err; } sc_log(card->ctx, "Decrypted APDU sw1=%02x sw2=%02x", apdu->sw1, apdu->sw2); sc_log_hex(card->ctx, "Decrypted APDU response data", apdu->resp, apdu->resplen); r = SC_SUCCESS; err: free(asn1); free(mac_data); if (data) { sc_mem_clear(data, buf_len); free(data); } return r; } static int iso_add_sm(struct iso_sm_ctx *sctx, sc_card_t *card, sc_apdu_t *apdu, sc_apdu_t **sm_apdu) { if (!card || !sctx) return SC_ERROR_INVALID_ARGUMENTS; 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."); 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) LOG_TEST_RET(card->ctx, sctx->pre_transmit(card, sctx, apdu), "Could not complete SM specific pre transmit routine"); LOG_TEST_RET(card->ctx, sm_encrypt(sctx, card, apdu, sm_apdu), "Could not encrypt APDU"); return SC_SUCCESS; } 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) LOG_TEST_RET(card->ctx, sctx->post_transmit(card, sctx, sm_apdu), "Could not complete SM specific post transmit routine"); LOG_TEST_RET(card->ctx, sm_decrypt(sctx, card, sm_apdu, apdu), "Could not decrypt APDU"); if (sctx->finish) LOG_TEST_RET(card->ctx, sctx->finish(card, sctx, apdu), "Could not complete SM specific post transmit routine"); return SC_SUCCESS; } int iso_sm_close(struct sc_card *card) { if (card) { iso_sm_ctx_clear_free(card->sm_ctx.info.cmd_data); card->sm_ctx.info.cmd_data = NULL; } return SC_SUCCESS; } int iso_get_sm_apdu(struct sc_card *card, struct sc_apdu *apdu, struct sc_apdu **sm_apdu) { return iso_add_sm(card->sm_ctx.info.cmd_data, card, apdu, sm_apdu); } int iso_free_sm_apdu(struct sc_card *card, struct sc_apdu *apdu, struct sc_apdu **sm_apdu) { struct sc_apdu *p; int r; if (!sm_apdu) return SC_ERROR_INVALID_ARGUMENTS; p = *sm_apdu; r = iso_rm_sm(card->sm_ctx.info.cmd_data, card, p, apdu); if (p) { free((unsigned char *) p->data); free((unsigned char *) p->resp); } free(*sm_apdu); *sm_apdu = NULL; return r; } struct iso_sm_ctx *iso_sm_ctx_create(void) { struct iso_sm_ctx *sctx = malloc(sizeof *sctx); if (!sctx) return NULL; sctx->priv_data = NULL; sctx->padding_indicator = SM_ISO_PADDING; sctx->block_length = 0; sctx->authenticate = NULL; sctx->verify_authentication = NULL; sctx->encrypt = NULL; sctx->decrypt = NULL; sctx->pre_transmit = NULL; sctx->post_transmit = NULL; sctx->finish = NULL; sctx->clear_free = NULL; return sctx; } void iso_sm_ctx_clear_free(struct iso_sm_ctx *sctx) { if (sctx && sctx->clear_free) sctx->clear_free(sctx); free(sctx); } int iso_sm_start(struct sc_card *card, struct iso_sm_ctx *sctx) { if (!card) return SC_ERROR_INVALID_ARGUMENTS; if (card->sm_ctx.ops.close) card->sm_ctx.ops.close(card); card->sm_ctx.info.cmd_data = sctx; card->sm_ctx.ops.close = iso_sm_close; card->sm_ctx.ops.free_sm_apdu = iso_free_sm_apdu; card->sm_ctx.ops.get_sm_apdu = iso_get_sm_apdu; card->sm_ctx.sm_mode = SM_MODE_TRANSMIT; return SC_SUCCESS; } #else int iso_sm_close(struct sc_card *card) { return SC_ERROR_NOT_SUPPORTED; } int iso_get_sm_apdu(struct sc_card *card, struct sc_apdu *apdu, struct sc_apdu **sm_apdu) { return SC_ERROR_NOT_SUPPORTED; } struct iso_sm_ctx *iso_sm_ctx_create(void) { return NULL; } void iso_sm_ctx_clear_free(struct iso_sm_ctx *sctx) { } int iso_sm_start(struct sc_card *card, struct iso_sm_ctx *sctx) { return SC_ERROR_NOT_SUPPORTED; } #endif OpenSC-0.26.1/src/sm/sm-iso.h000066400000000000000000000320661474147347300155420ustar00rootroot00000000000000/* * Copyright (C) 2012-2015 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 */ /** * @file * @defgroup sm Interface to Secure Messaging (SM) defined in ISO 7816 * @{ */ #ifndef _ISO_SM_H #define _ISO_SM_H #include "libopensc/opensc.h" #ifdef __cplusplus extern "C" { #endif /** @brief maximum length of response when targeting a SM RAPDU * * Using SM with authenticated data+le and encrypted data this is the biggest * amount of the unencrypted response data we can receive. We assume AES block * length for padding and MAC. */ #define MAX_SM_APDU_RESP_SIZE 223 /** @brief maximum length of data when targeting a SM APDU * * Using SM with authenticated data+header and encrypted data this is the * biggest amount of the unencrypted data we can send. We assume AES block * length for padding and MAC. */ #define MAX_SM_APDU_DATA_SIZE 239 /** @brief Padding indicator: use ISO/IEC 9797-1 padding method 2 */ #define SM_ISO_PADDING 0x01 /** @brief Padding indicator: use no padding */ #define SM_NO_PADDING 0x02 /** @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; /** @brief Padding-content indicator byte (ISO 7816-4 Table 30) */ u8 padding_indicator; /** @brief Pad to this block length */ size_t block_length; /** @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, 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); /** @brief Call back function for encryption of data */ int (*encrypt)(sc_card_t *card, const struct iso_sm_ctx *ctx, const u8 *data, size_t datalen, u8 **enc); /** @brief Call back function for decryption of data */ 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, * 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, * 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 */ int (*finish)(sc_card_t *card, const struct iso_sm_ctx *ctx, sc_apdu_t *apdu); /** @brief Clears and frees private data */ void (*clear_free)(const struct iso_sm_ctx *ctx); }; /** * @brief Clears and frees the SM context including private data * * Calls \a sctx->clear_free() if available * * @param[in] sctx (optional) */ void iso_sm_ctx_clear_free(struct iso_sm_ctx *sctx); /** * @brief Creates a SM context * * @return SM context or NULL if an error occurred */ struct iso_sm_ctx *iso_sm_ctx_create(void); /** * @brief Initializes a card for usage of the ISO SM driver * * If a SM module has been assigned previously to the card, it will be cleaned * up. * * @param[in] card * @param[in] sctx will NOT be freed automatically. \a sctx should be present * for the time of the SM session. * * @return \c SC_SUCCESS or error code if an error occurred */ int iso_sm_start(struct sc_card *card, struct iso_sm_ctx *sctx); int iso_sm_close(struct sc_card *card); #ifdef __cplusplus } #endif #endif /* @} */ OpenSC-0.26.1/src/smm/000077500000000000000000000000001474147347300143305ustar00rootroot00000000000000OpenSC-0.26.1/src/smm/Makefile.am000066400000000000000000000015371474147347300163720ustar00rootroot00000000000000# Process this file with automake to create Makefile.in MAINTAINERCLEANFILES = Makefile.in EXTRA_DIST = Makefile.mak smm-local.dll.manifest AM_CFLAGS = $(OPTIONAL_OPENSSL_CFLAGS) $(OPTIONAL_READLINE_CFLAGS) AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/common -I$(top_builddir)/src/include LIBS = $(top_builddir)/src/sm/libsm.la \ $(top_builddir)/src/libopensc/libopensc.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) libsmm_local_la_LDFLAGS = -module -shared -no-undefined -version-info @OPENSC_LT_CURRENT@:@OPENSC_LT_REVISION@:@OPENSC_LT_AGE@ # noinst_HEADERS = sm.h OpenSC-0.26.1/src/smm/Makefile.mak000066400000000000000000000021131474147347300165340ustar00rootroot00000000000000TOPDIR = ..\.. 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\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 Shell32.lib Comctl32.lib shlwapi.lib if EXIST $(TARGET).manifest mt -manifest $(TARGET).manifest -outputresource:$(TARGET);2 !ELSE $(TARGET): !ENDIF OpenSC-0.26.1/src/smm/sm-card-authentic.c000066400000000000000000000115301474147347300200040ustar00rootroot00000000000000/* * sm-authentic.c: Secure Messaging procedures specific to Oberthur's card * 'COSMO v7' with PKI applet 'AuthentIC v3.1' * * Copyright (C) 2010 Viktor Tarasov * OpenTrust * * 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 #include #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include #include #include "libopensc/opensc.h" #include "libopensc/log.h" #include "sm-module.h" static int sm_oberthur_diversify_keyset(struct sc_context *ctx, struct sm_info *sm_info, unsigned char *idata, size_t idata_len) { struct sm_gp_session *gp_session = &sm_info->session.gp; struct sm_gp_keyset *gp_keyset = &sm_info->session.gp.gp_keyset; unsigned char master_key[16] = { 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F, }; unsigned char *keys[3] = { gp_keyset->enc, gp_keyset->mac, gp_keyset->kek }; unsigned char key_buff[16]; unsigned char *tmp; int rv = 0, ii, tmp_len; 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) { if (gp_keyset->kmc_len == 16) memcpy(master_key, gp_keyset->kmc, 16); 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; key_buff[2] = key_buff[10] = *(idata + 6); key_buff[3] = key_buff[11] = *(idata + 7); key_buff[4] = key_buff[12] = *(idata + 8); key_buff[5] = key_buff[13] = *(idata + 9); key_buff[6] = 0xF0, key_buff[14] = 0x0F; key_buff[7] = key_buff[15] = ii+1; sc_debug(ctx, SC_LOG_DEBUG_SM, "key_buf:%s", sc_dump_hex(key_buff, 16)); 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 { LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "GP init session: invalid KMC data"); } 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; } static int sm_authentic_encode_apdu(struct sc_context *ctx, struct sm_info *sm_info) { struct sc_apdu *apdu = (struct sc_apdu *) sm_info->cmd_data; int rv = SC_ERROR_INVALID_ARGUMENTS; LOG_FUNC_CALLED(ctx); 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"); LOG_FUNC_RETURN(ctx, rv); } int sm_authentic_get_apdus(struct sc_context *ctx, struct sm_info *sm_info, unsigned char *init_data, size_t init_len, struct sc_remote_data *rdata, int release_sm) { int rv = 0; LOG_FUNC_CALLED(ctx); if (!sm_info) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); sc_debug(ctx, SC_LOG_DEBUG_SM, "SM get APDUs: rdata:%p, init_len:%"SC_FORMAT_LEN_SIZE_T"u", rdata, init_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); LOG_TEST_RET(ctx, rv, "SM get APDUs: cannot authenticate card"); } 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"); break; case SM_CMD_INITIALIZE: break; default: LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "unsupported SM command"); } LOG_FUNC_RETURN(ctx, rv); } OpenSC-0.26.1/src/smm/sm-card-iasecc.c000066400000000000000000000544351474147347300172620ustar00rootroot00000000000000/* * sm-iasecc.c: Secure Messaging procedures specific to IAS/ECC card * * Copyright (C) 2010 Viktor Tarasov * OpenTrust * * 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 #include #include #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include #include #include #include "libopensc/opensc.h" #include "libopensc/sm.h" #include "libopensc/log.h" #include "libopensc/asn1.h" #include "libopensc/iasecc.h" #include "libopensc/iasecc-sdo.h" #include "sm-module.h" static const struct sc_asn1_entry c_asn1_iasecc_sm_data_object[4] = { { "encryptedData", SC_ASN1_OCTET_STRING, SC_ASN1_CTX | 7, SC_ASN1_OPTIONAL, NULL, NULL }, { "commandStatus", SC_ASN1_OCTET_STRING, SC_ASN1_CTX | 0x19, 0, NULL, NULL }, { "ticket", SC_ASN1_OCTET_STRING, SC_ASN1_CTX | 0x0E, 0, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; static int sm_iasecc_get_apdu_read_binary(struct sc_context *ctx, struct sm_info *sm_info, struct sc_remote_data *rdata) { struct iasecc_sm_cmd_update_binary *cmd_data = (struct iasecc_sm_cmd_update_binary *)sm_info->cmd_data; size_t offs, data_offs = 0; int rv = 0; LOG_FUNC_CALLED(ctx); if (!cmd_data || !cmd_data->data) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); if (!rdata || !rdata->alloc) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); 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) { 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); LOG_TEST_RET(ctx, rv, "SM get 'READ BINARY' APDUs: cannot allocate remote APDU"); rapdu->apdu.cse = SC_APDU_CASE_2_SHORT; rapdu->apdu.cla = 0x00; rapdu->apdu.ins = 0xB0; rapdu->apdu.p1 = (offs >> 8) & 0xFF; rapdu->apdu.p2 = offs & 0xFF; rapdu->apdu.le = sz; /* 'resplen' is set by remote apdu allocation procedure */ rv = sm_cwa_securize_apdu(ctx, sm_info, rapdu); LOG_TEST_RET(ctx, rv, "SM get 'UPDATE BINARY' APDUs: securize APDU error"); rapdu->flags |= SC_REMOTE_APDU_FLAG_RETURN_ANSWER; offs += sz; data_offs += sz; } LOG_FUNC_RETURN(ctx, rv); } static int sm_iasecc_get_apdu_update_binary(struct sc_context *ctx, struct sm_info *sm_info, struct sc_remote_data *rdata) { struct iasecc_sm_cmd_update_binary *cmd_data = (struct iasecc_sm_cmd_update_binary *)sm_info->cmd_data; size_t offs, data_offs = 0; int rv = 0; LOG_FUNC_CALLED(ctx); if (!cmd_data || !cmd_data->data) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); if (!rdata || !rdata->alloc) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); 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) { 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); LOG_TEST_RET(ctx, rv, "SM get 'UPDATE BINARY' APDUs: cannot allocate remote APDU"); rapdu->apdu.cse = SC_APDU_CASE_3_SHORT; rapdu->apdu.cla = 0x00; rapdu->apdu.ins = 0xD6; rapdu->apdu.p1 = (offs >> 8) & 0xFF; rapdu->apdu.p2 = offs & 0xFF; memcpy((unsigned char *)rapdu->apdu.data, cmd_data->data + data_offs, sz); rapdu->apdu.datalen = sz; rapdu->apdu.lc = sz; /** 99 02 SW 8E 08 MAC **/ rapdu->apdu.le = 0x0E; rv = sm_cwa_securize_apdu(ctx, sm_info, rapdu); LOG_TEST_RET(ctx, rv, "SM get 'UPDATE BINARY' APDUs: securize APDU error"); rapdu->flags |= SC_REMOTE_APDU_FLAG_RETURN_ANSWER; offs += sz; data_offs += sz; } LOG_FUNC_RETURN(ctx, rv); } /* TODO: reduce name of functions */ static int sm_iasecc_get_apdu_create_file(struct sc_context *ctx, struct sm_info *sm_info, struct sc_remote_data *rdata) { struct iasecc_sm_cmd_create_file *cmd_data = (struct iasecc_sm_cmd_create_file *)sm_info->cmd_data; struct sc_remote_apdu *rapdu = NULL; int rv = 0; LOG_FUNC_CALLED(ctx); if (!cmd_data || !cmd_data->data || !rdata || !rdata->alloc) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); 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)); rv = rdata->alloc(rdata, &rapdu); LOG_TEST_RET(ctx, rv, "SM get 'UPDATE BINARY' APDUs: cannot allocate remote APDU"); rapdu->apdu.cse = SC_APDU_CASE_3_SHORT; rapdu->apdu.cla = 0x00; rapdu->apdu.ins = 0xE0; rapdu->apdu.p1 = 0x00; rapdu->apdu.p2 = 0x00; memcpy((unsigned char *)rapdu->apdu.data, cmd_data->data, cmd_data->size); rapdu->apdu.datalen = cmd_data->size; rapdu->apdu.lc = cmd_data->size; /** 99 02 SW 8E 08 MAC **/ rapdu->apdu.le = 0x0E; rv = sm_cwa_securize_apdu(ctx, sm_info, rapdu); LOG_TEST_RET(ctx, rv, "SM get 'UPDATE BINARY' APDUs: securize APDU error"); rapdu->flags |= SC_REMOTE_APDU_FLAG_RETURN_ANSWER; LOG_FUNC_RETURN(ctx, rv); } static int sm_iasecc_get_apdu_delete_file(struct sc_context *ctx, struct sm_info *sm_info, struct sc_remote_data *rdata) { unsigned int file_id = (unsigned int)(uintptr_t)sm_info->cmd_data; struct sc_remote_apdu *rapdu = NULL; int rv; LOG_FUNC_CALLED(ctx); 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); if (!rdata || !rdata->alloc) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); rv = rdata->alloc(rdata, &rapdu); LOG_TEST_RET(ctx, rv, "SM get 'DELETE FILE' APDUs: cannot allocate remote APDU"); rapdu->apdu.cse = SC_APDU_CASE_1; rapdu->apdu.cla = 0x00; rapdu->apdu.ins = 0xE4; rapdu->apdu.p1 = 0x00; rapdu->apdu.p2 = 0x00; /** 99 02 SW 8E 08 MAC **/ rapdu->apdu.le = 0x0E; rv = sm_cwa_securize_apdu(ctx, sm_info, rapdu); LOG_TEST_RET(ctx, rv, "SM get 'DELETE FILE' APDUs: securize APDU error"); rapdu->flags |= SC_REMOTE_APDU_FLAG_RETURN_ANSWER; LOG_FUNC_RETURN(ctx, rv); } static int sm_iasecc_get_apdu_verify_pin(struct sc_context *ctx, struct sm_info *sm_info, struct sc_remote_data *rdata) { struct sc_pin_cmd_data *pin_data = (struct sc_pin_cmd_data *)sm_info->cmd_data; struct sc_remote_apdu *rapdu = NULL; int rv; LOG_FUNC_CALLED(ctx); if (!pin_data || !rdata || !rdata->alloc) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); 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"); rapdu->apdu.cse = SC_APDU_CASE_3_SHORT; rapdu->apdu.cla = 0x00; rapdu->apdu.ins = 0x20; 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: invalid PIN size"); memcpy((unsigned char *)rapdu->apdu.data, pin_data->pin1.data, pin_data->pin1.len); rapdu->apdu.datalen = pin_data->pin1.len; rapdu->apdu.lc = pin_data->pin1.len; /** 99 02 SW 8E 08 MAC **/ rapdu->apdu.le = 0x0E; rv = sm_cwa_securize_apdu(ctx, sm_info, rapdu); LOG_TEST_RET(ctx, rv, "SM get 'VERIFY PIN' APDUs: securize APDU error"); rapdu->flags |= SC_REMOTE_APDU_FLAG_RETURN_ANSWER; LOG_FUNC_RETURN(ctx, rv); } static int sm_iasecc_get_apdu_reset_pin(struct sc_context *ctx, struct sm_info *sm_info, struct sc_remote_data *rdata) { struct sc_pin_cmd_data *pin_data = (struct sc_pin_cmd_data *)sm_info->cmd_data; struct sc_remote_apdu *rapdu = NULL; int rv; LOG_FUNC_CALLED(ctx); if (!pin_data || !rdata || !rdata->alloc) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); 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"); rapdu->apdu.cse = SC_APDU_CASE_3_SHORT; rapdu->apdu.cla = 0x00; rapdu->apdu.ins = 0x2C; rapdu->apdu.p2 = pin_data->pin_reference & ~IASECC_OBJECT_REF_GLOBAL; if (pin_data->pin2.len) { if (pin_data->pin2.len > SM_MAX_DATA_SIZE) LOG_TEST_RET(ctx, rv, "SM get 'RESET PIN' APDU: invalid PIN size"); rapdu->apdu.p1 = 0x02; memcpy((unsigned char *)rapdu->apdu.data, pin_data->pin2.data, pin_data->pin2.len); rapdu->apdu.datalen = pin_data->pin2.len; rapdu->apdu.lc = pin_data->pin2.len; } else { rapdu->apdu.p1 = 0x03; } /** 99 02 SW 8E 08 MAC **/ rapdu->apdu.le = 0x0E; rv = sm_cwa_securize_apdu(ctx, sm_info, rapdu); LOG_TEST_RET(ctx, rv, "SM get 'RESET PIN' APDUs: securize APDU error"); rapdu->flags |= SC_REMOTE_APDU_FLAG_RETURN_ANSWER; LOG_FUNC_RETURN(ctx, rv); } static int sm_iasecc_get_apdu_sdo_update(struct sc_context *ctx, struct sm_info *sm_info, struct sc_remote_data *rdata) { struct iasecc_sdo_update *update = (struct iasecc_sdo_update *)sm_info->cmd_data; int rv = SC_ERROR_INVALID_ARGUMENTS, ii; LOG_FUNC_CALLED(ctx); if (!update) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); if (!rdata || !rdata->alloc) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); 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; 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_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; ) { 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); LOG_TEST_RET(ctx, rv, "SM get 'SDO UPDATE' APDUs: cannot allocate remote APDU"); rapdu->apdu.cse = SC_APDU_CASE_3_SHORT; rapdu->apdu.cla = len + offs < encoded_len ? SC_APDU_FLAGS_CHAINING : 0x00; rapdu->apdu.ins = 0xDB; rapdu->apdu.p1 = 0x3F; rapdu->apdu.p2 = 0xFF; memcpy((unsigned char *)rapdu->apdu.data, encoded + offs, len); rapdu->apdu.datalen = len; rapdu->apdu.lc = len; /** 99 02 SW 8E 08 MAC **/ rapdu->apdu.le = 0x0E; rv = sm_cwa_securize_apdu(ctx, sm_info, rapdu); LOG_TEST_RET(ctx, rv, "SM get 'SDO UPDATE' APDUs: securize APDU error"); rapdu->flags |= SC_REMOTE_APDU_FLAG_RETURN_ANSWER; offs += len; } free(encoded); } LOG_FUNC_RETURN(ctx, rv); } static int sm_iasecc_get_apdu_generate_rsa(struct sc_context *ctx, struct sm_info *sm_info, struct sc_remote_data *rdata) { struct iasecc_sdo *sdo = (struct iasecc_sdo *)sm_info->cmd_data; struct sc_remote_apdu *rapdu = NULL; unsigned char put_exponent_data[14] = { 0x70, 0x0C, IASECC_SDO_TAG_HEADER, IASECC_SDO_CLASS_RSA_PUBLIC | 0x80, sdo->sdo_ref & 0x7F, 0x08, 0x7F, 0x49, 0x05, 0x82, 0x03, 0x01, 0x00, 0x01 }; unsigned char generate_data[5] = { 0x70, 0x03, IASECC_SDO_TAG_HEADER, IASECC_SDO_CLASS_RSA_PRIVATE | 0x80, sdo->sdo_ref & 0x7F }; int rv; LOG_FUNC_CALLED(ctx); 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); /* Put Exponent */ rv = rdata->alloc(rdata, &rapdu); LOG_TEST_RET(ctx, rv, "SM get 'UPDATE BINARY' APDUs: cannot allocate remote APDU"); rapdu->apdu.cse = SC_APDU_CASE_3_SHORT; rapdu->apdu.cla = 0x00; rapdu->apdu.ins = 0xDB; rapdu->apdu.p1 = 0x3F; rapdu->apdu.p2 = 0xFF; memcpy((unsigned char *)rapdu->apdu.data, put_exponent_data, sizeof(put_exponent_data)); rapdu->apdu.datalen = sizeof(put_exponent_data); rapdu->apdu.lc = sizeof(put_exponent_data); /** 99 02 SW 8E 08 MAC **/ rapdu->apdu.le = 0x0E; rv = sm_cwa_securize_apdu(ctx, sm_info, rapdu); LOG_TEST_RET(ctx, rv, "SM get 'UPDATE BINARY' APDUs: securize APDU error"); rapdu->flags |= SC_REMOTE_APDU_FLAG_RETURN_ANSWER; /* Generate Key */ rv = rdata->alloc(rdata, &rapdu); LOG_TEST_RET(ctx, rv, "SM get 'UPDATE BINARY' APDUs: cannot allocate remote APDU"); rapdu->apdu.cse = SC_APDU_CASE_4_SHORT; rapdu->apdu.cla = 0x00; rapdu->apdu.ins = 0x47; rapdu->apdu.p1 = 0x00; rapdu->apdu.p2 = 0x00; memcpy((unsigned char *)rapdu->apdu.data, generate_data, sizeof(generate_data)); rapdu->apdu.datalen = sizeof(generate_data); rapdu->apdu.lc = sizeof(generate_data); rapdu->apdu.le = 0x100; rv = sm_cwa_securize_apdu(ctx, sm_info, rapdu); LOG_TEST_RET(ctx, rv, "SM get 'UPDATE BINARY' APDUs: securize APDU error"); rapdu->flags |= SC_REMOTE_APDU_FLAG_RETURN_ANSWER; LOG_FUNC_RETURN(ctx, rv); } static int sm_iasecc_get_apdu_update_rsa(struct sc_context *ctx, struct sm_info *sm_info, struct sc_remote_data *rdata) { struct iasecc_sdo_rsa_update *cmd_data = (struct iasecc_sdo_rsa_update *)sm_info->cmd_data; struct iasecc_sdo_update *to_update[2] = {NULL, NULL}; int rv = 0, ii = 0, jj = 0; LOG_FUNC_CALLED(ctx); if (cmd_data->update_prv.sdo_class) { to_update[ii++] = &cmd_data->update_prv; 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_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; ii < IASECC_SDO_TAGS_UPDATE_MAX && to_update[jj]->fields[ii].tag; ii++) { unsigned char *encoded = NULL; size_t encoded_len, offs; 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); 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, rv, "SM get 'UPDATE RSA' APDU: cannot encode key component"); encoded_len = rv; 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; ) { 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); LOG_TEST_RET(ctx, rv, "SM get 'UPDATE RSA' APDUs: cannot allocate remote APDU"); rapdu->apdu.cse = SC_APDU_CASE_3_SHORT; rapdu->apdu.cla = len + offs < encoded_len ? 0x10 : 0x00; rapdu->apdu.ins = 0xDB; rapdu->apdu.p1 = 0x3F; rapdu->apdu.p2 = 0xFF; memcpy((unsigned char *)rapdu->apdu.data, encoded + offs, len); rapdu->apdu.datalen = len; rapdu->apdu.lc = len; /** 99 02 SW 8E 08 MAC **/ rapdu->apdu.le = 0x0E; rv = sm_cwa_securize_apdu(ctx, sm_info, rapdu); LOG_TEST_RET(ctx, rv, "SM get 'UPDATE RSA' APDUs: securize APDU error"); rapdu->flags |= SC_REMOTE_APDU_FLAG_RETURN_ANSWER; offs += len; } free(encoded); } } LOG_FUNC_RETURN(ctx, SC_SUCCESS); } int sm_iasecc_get_apdus(struct sc_context *ctx, struct sm_info *sm_info, unsigned char *init_data, size_t init_len, struct sc_remote_data *rdata, int release_sm) { struct sm_cwa_session *cwa_session = &sm_info->session.cwa; struct sm_cwa_keyset *cwa_keyset = &sm_info->session.cwa.cwa_keyset; int rv; LOG_FUNC_CALLED(ctx); if (!sm_info) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); sc_debug(ctx, SC_LOG_DEBUG_SM, "SM IAS/ECC get APDUs: init_len:%"SC_FORMAT_LEN_SIZE_T"u", init_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"); 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_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: rv = sm_iasecc_get_apdu_read_binary(ctx, sm_info, rdata); LOG_TEST_RET(ctx, rv, "SM IAS/ECC get APDUs: 'READ BINARY' failed"); break; case SM_CMD_FILE_UPDATE: rv = sm_iasecc_get_apdu_update_binary(ctx, sm_info, rdata); LOG_TEST_RET(ctx, rv, "SM IAS/ECC get APDUs: 'UPDATE BINARY' failed"); break; case SM_CMD_FILE_CREATE: rv = sm_iasecc_get_apdu_create_file(ctx, sm_info, rdata); LOG_TEST_RET(ctx, rv, "SM IAS/ECC get APDUs: 'CREATE FILE' failed"); break; case SM_CMD_FILE_DELETE: rv = sm_iasecc_get_apdu_delete_file(ctx, sm_info, rdata); LOG_TEST_RET(ctx, rv, "SM IAS/ECC get APDUs: 'DELETE FILE' failed"); break; case SM_CMD_PIN_RESET: rv = sm_iasecc_get_apdu_reset_pin(ctx, sm_info, rdata); LOG_TEST_RET(ctx, rv, "SM IAS/ECC get APDUs: 'RESET PIN' failed"); break; case SM_CMD_RSA_GENERATE: rv = sm_iasecc_get_apdu_generate_rsa(ctx, sm_info, rdata); LOG_TEST_RET(ctx, rv, "SM IAS/ECC get APDUs: 'GENERATE RSA' failed"); break; case SM_CMD_RSA_UPDATE: rv = sm_iasecc_get_apdu_update_rsa(ctx, sm_info, rdata); LOG_TEST_RET(ctx, rv, "SM IAS/ECC get APDUs: 'UPDATE RSA' failed"); break; case SM_CMD_SDO_UPDATE: rv = sm_iasecc_get_apdu_sdo_update(ctx, sm_info, rdata); LOG_TEST_RET(ctx, rv, "SM IAS/ECC get APDUs: 'SDO UPDATE' failed"); break; case SM_CMD_PIN_VERIFY: rv = sm_iasecc_get_apdu_verify_pin(ctx, sm_info, rdata); LOG_TEST_RET(ctx, rv, "SM IAS/ECC get APDUs: 'RAW APDU' failed"); break; default: LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "unsupported SM command"); } if (release_sm) { /* Apparently useless for this card */ } LOG_FUNC_RETURN(ctx, rv); } int sm_iasecc_decode_card_data(struct sc_context *ctx, struct sm_info *sm_info, struct sc_remote_data *rdata, unsigned char *out, size_t out_len) { struct sm_cwa_session *session_data = &sm_info->session.cwa; struct sc_asn1_entry asn1_iasecc_sm_data_object[4]; struct sc_remote_apdu *rapdu = NULL; int rv, offs = 0; LOG_FUNC_CALLED(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) { unsigned char *decrypted; size_t decrypted_len; unsigned char resp_data[SC_MAX_APDU_BUFFER_SIZE]; size_t resp_len = sizeof(resp_data); unsigned char status[2] = {0, 0}; size_t status_len = sizeof(status); unsigned char ticket[8]; size_t ticket_len = sizeof(ticket); 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)); sc_copy_asn1_entry(c_asn1_iasecc_sm_data_object, asn1_iasecc_sm_data_object); sc_format_asn1_entry(asn1_iasecc_sm_data_object + 0, resp_data, &resp_len, 0); sc_format_asn1_entry(asn1_iasecc_sm_data_object + 1, status, &status_len, 0); sc_format_asn1_entry(asn1_iasecc_sm_data_object + 2, ticket, &ticket_len, 0); 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_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_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"); decrypted_len = sizeof(decrypted); rv = sm_decrypt_des_cbc3(ctx, session_data->session_enc, &resp_data[1], resp_len - 1, &decrypted, &decrypted_len); LOG_TEST_RET(ctx, rv, "IAS/ECC decode answer(s): cannot decrypt card answer data"); 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)); while(*(decrypted + decrypted_len - 1) == 0x00) decrypted_len--; if (*(decrypted + decrypted_len - 1) != 0x80) LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "IAS/ECC decode answer(s): invalid card data padding "); decrypted_len--; if (out && out_len) { if (out_len < offs + decrypted_len) 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_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); } free(decrypted); } } LOG_FUNC_RETURN(ctx, offs); } OpenSC-0.26.1/src/smm/sm-common.exports000066400000000000000000000000441474147347300176610ustar00rootroot00000000000000sm_cwa_get_mac sm_cwa_securize_apdu OpenSC-0.26.1/src/smm/sm-cwa14890.c000066400000000000000000000321421474147347300162730ustar00rootroot00000000000000/* * sm-cwa14890.c: Procedures related to Secure Messaging according to the CWA-14890 * * Copyright (C) 2010 Viktor Tarasov * OpenTrust * * 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 #include #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include #include #include #include "libopensc/opensc.h" #include "libopensc/sm.h" #include "libopensc/log.h" #include "libopensc/asn1.h" #include "libopensc/iasecc.h" #include "libopensc/iasecc-sdo.h" #include "sm-module.h" int 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) { unsigned char padding[8] = {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; unsigned char *buf; LOG_FUNC_CALLED(ctx); 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_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); if (force_padding) in_len = ((in_len + 8) / 8) * 8; else in_len = ((in_len + 7) / 8) * 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)); DES_cbc_cksum_3des_emv96(ctx, buf, out, in_len, key, icv); free(buf); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int sm_cwa_encode_external_auth_data(struct sc_context *ctx, struct sm_cwa_session *session_data, unsigned char *out, size_t out_len) { if (out_len < 16) return SC_ERROR_BUFFER_TOO_SMALL; 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); return 16; } int sm_cwa_encode_mutual_auth_data(struct sc_context *ctx, struct sm_cwa_session *session_data, unsigned char *out, size_t out_len) { if (out_len < 64) return SC_ERROR_BUFFER_TOO_SMALL; 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); memcpy(out + 16, session_data->icc.rnd, 8); memcpy(out + 24, session_data->icc.sn, 8); memcpy(out + 32, session_data->ifd.k, 32); return 64; } 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) { 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; LOG_FUNC_CALLED(ctx); 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_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); 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_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)); if (memcmp(decrypted, session_data->icc.rnd, 8)) { free(decrypted); LOG_FUNC_RETURN(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); } if (memcmp(decrypted + 8, session_data->icc.sn, 8)) { free(decrypted); LOG_FUNC_RETURN(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); } if (memcmp(decrypted + 16, session_data->ifd.rnd, 8)) { free(decrypted); LOG_FUNC_RETURN(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); } if (memcmp(decrypted + 24, session_data->ifd.sn, 8)) { free(decrypted); LOG_FUNC_RETURN(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); } memcpy(session_data->icc.k, decrypted + 32, 32); free(decrypted); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } int sm_cwa_init_session_keys(struct sc_context *ctx, struct sm_cwa_session *session_data, unsigned char mechanism) { unsigned char xored[36]; unsigned char buff[SHA256_DIGEST_LENGTH]; int ii; memset(xored, 0, sizeof(xored)); for (ii=0; ii<32; ii++) xored[ii] = session_data->ifd.k[ii] ^ session_data->icc.k[ii]; 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_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_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) { xored[35] = 0x01; SHA256(xored, 36, buff); memcpy(&session_data->session_enc[0], buff, sizeof(session_data->session_enc)); xored[35] = 0x02; SHA256(xored, 36, buff); memcpy(&session_data->session_mac[0], buff, sizeof(session_data->session_mac)); } else { return SC_ERROR_INVALID_ARGUMENTS; } memcpy(session_data->ssc + 0, session_data->icc.rnd + 4, 4); memcpy(session_data->ssc + 4, session_data->ifd.rnd + 4, 4); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } int sm_cwa_initialize(struct sc_context *ctx, struct sm_info *sm_info, struct sc_remote_data *rdata) { struct sm_cwa_session *cwa_session = &sm_info->session.cwa; struct sm_cwa_keyset *cwa_keyset = &sm_info->session.cwa.cwa_keyset; struct sc_serial_number sn = sm_info->serialnr; 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 = NULL; size_t encrypted_len; sm_des_cblock icv = {0, 0, 0, 0, 0, 0, 0, 0}, cblock; int rv, offs; LOG_FUNC_CALLED(ctx); 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); rv = rdata->alloc(rdata, &new_rapdu); LOG_TEST_RET(ctx, rv, "SM GP decode card answer: cannot allocate remote APDU"); apdu = &new_rapdu->apdu; memcpy(&cwa_session->icc.rnd[0], cwa_session->card_challenge, 8); if (sn.len > icc_sn_len) memcpy(&cwa_session->icc.sn[0], &sn.value[sn.len - icc_sn_len], icc_sn_len); else memcpy(&cwa_session->icc.sn[icc_sn_len - sn.len], &sn.value[0], sn.len); if (sm_info->cmd == SM_CMD_EXTERNAL_AUTH) { offs = sm_cwa_encode_external_auth_data(ctx, cwa_session, buf, sizeof(buf)); if (offs != 0x10) LOG_FUNC_RETURN(ctx, offs); } else { offs = sm_cwa_encode_mutual_auth_data(ctx, cwa_session, buf, sizeof(buf)); if (offs != 0x40) LOG_FUNC_RETURN(ctx, 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_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 = (int)encrypted_len; rv = sm_cwa_get_mac(ctx, cwa_keyset->mac, &icv, buf, offs, &cblock, 1); 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; apdu->cla = 0x00; apdu->ins = 0x82; apdu->p1 = 0x00; apdu->p2 = 0x00; apdu->lc = encrypted_len + sizeof(cblock); apdu->le = encrypted_len + sizeof(cblock); 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, rv); } int sm_cwa_securize_apdu(struct sc_context *ctx, struct sm_info *sm_info, struct sc_remote_apdu *rapdu) { struct sm_cwa_session *session_data = &sm_info->session.cwa; struct sc_apdu *apdu = &rapdu->apdu; unsigned char sbuf[0x400]; sm_des_cblock cblock, icv; unsigned char *encrypted = NULL, edfb_data[0x200], mac_data[0x200]; size_t encrypted_len, edfb_len = 0, offs; int rv, mac_len = 0; LOG_FUNC_CALLED(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); sm_incr_ssc(session_data->ssc, sizeof(session_data->ssc)); 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_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; if (apdu->ins & 0x01) { edfb_data[offs++] = IASECC_SM_DO_TAG_TCG_ODD_INS; if (encrypted_len + 1 > 0x7F) edfb_data[offs++] = 0x81; edfb_data[offs++] = encrypted_len; } else { edfb_data[offs++] = IASECC_SM_DO_TAG_TCG_EVEN_INS; if (encrypted_len + 1 > 0x7F) edfb_data[offs++] = 0x81; edfb_data[offs++] = encrypted_len + 1; edfb_data[offs++] = 0x01; } memcpy(edfb_data + offs, encrypted, encrypted_len); offs += encrypted_len; edfb_len = offs; 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); encrypted = NULL; offs = 0; memcpy(mac_data + offs, session_data->ssc, 8); offs += 8; mac_data[offs++] = apdu->cla | 0x0C; mac_data[offs++] = apdu->ins; mac_data[offs++] = apdu->p1; mac_data[offs++] = apdu->p2; mac_data[offs++] = 0x80; mac_data[offs++] = 0x00; mac_data[offs++] = 0x00; mac_data[offs++] = 0x00; memcpy(mac_data + offs, edfb_data, edfb_len); offs += edfb_len; /* if (apdu->le) { */ mac_data[offs++] = IASECC_SM_DO_TAG_TLE; mac_data[offs++] = 1; mac_data[offs++] = apdu->le; /* } */ 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_debug(ctx, SC_LOG_DEBUG_SM, "securize APDU: MAC:%s", sc_dump_hex(cblock, sizeof(cblock))); offs = 0; if (edfb_len) { memcpy(sbuf + offs, edfb_data, edfb_len); offs += edfb_len; } /* if (apdu->le) { */ sbuf[offs++] = IASECC_SM_DO_TAG_TLE; sbuf[offs++] = 1; sbuf[offs++] = apdu->le; /* } */ sbuf[offs++] = IASECC_SM_DO_TAG_TCC; sbuf[offs++] = 8; memcpy(sbuf + offs, cblock, 8); offs += 8; 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)) LOG_TEST_RET(ctx, SC_ERROR_BUFFER_TOO_SMALL, "securize APDU: buffer too small for encrypted data"); apdu->cse = SC_APDU_CASE_4_SHORT; apdu->cla |= 0x0C; apdu->lc = offs; apdu->datalen = offs; memcpy((unsigned char *)apdu->data, sbuf, offs); sm_incr_ssc(session_data->ssc, sizeof(session_data->ssc)); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } OpenSC-0.26.1/src/smm/sm-global-platform.c000066400000000000000000000321431474147347300201760ustar00rootroot00000000000000/* * sm-global-platform.c: Global Platform related procedures * * Copyright (C) 2010 Viktor Tarasov * OpenTrust * * 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 #include #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include #include #include #include #include "libopensc/opensc.h" #include "libopensc/sm.h" #include "libopensc/log.h" #include "libopensc/asn1.h" #include "sm-module.h" int sm_gp_decode_card_answer(struct sc_context *ctx, struct sc_remote_data *rdata, unsigned char *out, size_t out_len) { LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); } int sm_gp_initialize(struct sc_context *ctx, struct sm_info *sm_info, struct sc_remote_data *rdata) { struct sc_serial_number sn = sm_info->serialnr; struct sm_gp_session *gp_session = &sm_info->session.gp; struct sm_gp_keyset *gp_keyset = &sm_info->session.gp.gp_keyset; struct sc_remote_apdu *new_rapdu = NULL; struct sc_apdu *apdu = NULL; int rv; LOG_FUNC_CALLED(ctx); 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); rv = rdata->alloc(rdata, &new_rapdu); LOG_TEST_RET(ctx, rv, "SM GP decode card answer: cannot allocate remote APDU"); apdu = &new_rapdu->apdu; rv = RAND_bytes(gp_session->host_challenge, SM_SMALL_CHALLENGE_LEN); if (!rv) LOG_FUNC_RETURN(ctx, SC_ERROR_SM_RAND_FAILED); apdu->cse = SC_APDU_CASE_4_SHORT; apdu->cla = 0x80; apdu->ins = 0x50; apdu->p1 = 0x0; apdu->p2 = 0x0; apdu->lc = SM_SMALL_CHALLENGE_LEN; apdu->le = 0x1C; apdu->datalen = SM_SMALL_CHALLENGE_LEN; memcpy(&new_rapdu->sbuf[0], gp_session->host_challenge, SM_SMALL_CHALLENGE_LEN); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static unsigned char * sc_gp_get_session_key(struct sc_context *ctx, struct sm_gp_session *gp_session, unsigned char *key) { int out_len; unsigned char *out = NULL; unsigned char deriv[16]; memcpy(deriv, gp_session->card_challenge + 4, 4); memcpy(deriv + 4, gp_session->host_challenge, 4); memcpy(deriv + 8, gp_session->card_challenge, 4); memcpy(deriv + 12, gp_session->host_challenge + 4, 4); if (sm_encrypt_des_ecb3(ctx, key, deriv, 16, &out, &out_len)) { if (ctx) 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_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; } return 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) { unsigned char block[24]; sm_des_cblock cksum={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; if (out_len!=8) return SC_ERROR_INVALID_ARGUMENTS; memcpy(block + 0, left, 8); memcpy(block + 8, right, 8); memcpy(block + 16, "\x80\0\0\0\0\0\0\0",8); DES_cbc_cksum_3des(ctx, block, &cksum, sizeof(block), session_key, &cksum); memcpy(out, cksum, 8); return 0; } 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 len; unsigned char *block; block = malloc(in_len + 8); if (!block) return SC_ERROR_OUT_OF_MEMORY; memcpy(block, in, in_len); memcpy(block + in_len, "\x80\0\0\0\0\0\0\0", 8); len = in_len + 8; len -= (len%8); DES_cbc_cksum_3des(ctx, block, out, len, key, icv); free(block); return 0; } static int sm_gp_parse_init_data(struct sc_context *ctx, struct sm_gp_session *gp_session, unsigned char *init_data, size_t init_len) { struct sm_gp_keyset *gp_keyset = &gp_session->gp_keyset; if(init_len != 0x1C) return SC_ERROR_INVALID_DATA; gp_keyset->version = *(init_data + 10); gp_keyset->index = *(init_data + 11); memcpy(gp_session->card_challenge, init_data + 12, SM_SMALL_CHALLENGE_LEN); return SC_SUCCESS; } static int sm_gp_init_session(struct sc_context *ctx, struct sm_gp_session *gp_session, unsigned char *adata, size_t adata_len) { struct sm_gp_keyset *gp_keyset = &gp_session->gp_keyset; unsigned char cksum[8]; int rv; LOG_FUNC_CALLED(ctx); if (!adata || adata_len < 8) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); 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); gp_session->session_kek = sc_gp_get_session_key(ctx, gp_session, gp_keyset->kek); if (!gp_session->session_enc || !gp_session->session_mac || !gp_session->session_kek) 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_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(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_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_debug(ctx, SC_LOG_DEBUG_SM, "SM GP init session: card authenticated"); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } void sm_gp_close_session(struct sc_context *ctx, struct sm_gp_session *gp_session) { free(gp_session->session_enc); free(gp_session->session_mac); free(gp_session->session_kek); } int sm_gp_external_authentication(struct sc_context *ctx, struct sm_info *sm_info, unsigned char *init_data, size_t init_len, struct sc_remote_data *rdata, int (*diversify_keyset)(struct sc_context *ctx, struct sm_info *sm_info, unsigned char *idata, size_t idata_len)) { struct sc_remote_apdu *new_rapdu = NULL; 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; sm_des_cblock mac; int rv, offs = 0; LOG_FUNC_CALLED(ctx); if (!sm_info || !init_data || !rdata || !rdata->alloc) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); if (init_len != 0x1C) LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "SM GP authentication: invalid auth data length"); rv = sm_gp_parse_init_data(ctx, gp_session, init_data, init_len); LOG_TEST_RET(ctx, rv, "SM GP authentication: 'INIT DATA' parse error"); if (diversify_keyset) { rv = (*diversify_keyset)(ctx, sm_info, init_data, init_len); LOG_TEST_RET(ctx, rv, "SM GP authentication: keyset diversification error"); } 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(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_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"); apdu = &new_rapdu->apdu; offs = 0; apdu->cse = SC_APDU_CASE_3_SHORT; apdu->cla = raw_apdu[offs++] = 0x84; apdu->ins = raw_apdu[offs++] = 0x82; apdu->p1 = raw_apdu[offs++] = gp_session->params.level; apdu->p2 = raw_apdu[offs++] = 0; apdu->lc = raw_apdu[offs++] = 0x10; apdu->datalen = 0x10; memcpy(raw_apdu + offs, host_cryptogram, 8); offs += 8; 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); memcpy(new_rapdu->sbuf + 8, mac, 8); memcpy(gp_session->mac_icv, mac, 8); LOG_FUNC_RETURN(ctx, 1); } static int 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; size_t len; if (!out || !out_len) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "SM GP encrypt command data error"); 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) { *out = NULL; *out_len = 0; LOG_FUNC_RETURN(ctx, SC_SUCCESS); } len = in_len + 8; len -= (len % 8); data = calloc(1, len); if (!data) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); *data = in_len; memcpy(data + 1, in, in_len); rv = sm_encrypt_des_cbc3(ctx, session_key, data, in_len + 1, out, out_len, 1); free(data); LOG_TEST_RET(ctx, rv, "SM GP encrypt command data: encryption error"); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } int sm_gp_securize_apdu(struct sc_context *ctx, struct sm_info *sm_info, char *init_data, struct sc_apdu *apdu) { unsigned char buff[SC_MAX_APDU_BUFFER_SIZE + 24]; unsigned char *apdu_data = NULL; 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; sm_des_cblock mac; unsigned char *encrypted = NULL; size_t encrypted_len = 0; int rv; LOG_FUNC_CALLED(ctx); apdu_data = (unsigned char *)apdu->data; 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); if (gp_level == 0 || (apdu->cla & 0x04)) return 0; if (gp_level == SM_GP_SECURITY_MAC) { if (apdu->datalen + 8 > SC_MAX_APDU_BUFFER_SIZE) LOG_TEST_RET(ctx, SC_ERROR_WRONG_LENGTH, "SM GP securize APDU: too much data"); } else if (gp_level == SM_GP_SECURITY_ENC) { if (!gp_session->session_enc) LOG_TEST_RET(ctx, SC_ERROR_SM_INVALID_SESSION_KEY, "SM GP securize APDU: no ENC session key found"); 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) { rv = SC_ERROR_BUFFER_TOO_SMALL; LOG_TEST_GOTO_ERR(ctx, rv, "SM GP securize APDU: not enough place for encrypted data"); } sc_debug(ctx, SC_LOG_DEBUG_SM, "SM GP securize APDU: encrypted length %"SC_FORMAT_LEN_SIZE_T"u", encrypted_len); } else { LOG_TEST_RET(ctx, SC_ERROR_SM_INVALID_LEVEL, "SM GP securize APDU: invalid SM level"); } buff[0] = apdu->cla | 0x04; buff[1] = apdu->ins; buff[2] = apdu->p1; buff[3] = apdu->p2; buff[4] = apdu->lc + 8; memcpy(buff + 5, apdu_data, apdu->datalen); 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); apdu->cla |= 0x04; apdu->datalen += 8; apdu->lc = apdu->datalen; if (apdu->cse==SC_APDU_CASE_2_SHORT) apdu->cse = SC_APDU_CASE_4_SHORT; } else if (gp_level == SM_GP_SECURITY_ENC) { memcpy(apdu_data + encrypted_len, mac, 8); if (encrypted_len) memcpy(apdu_data, encrypted, encrypted_len); apdu->cla |= 0x04; apdu->datalen = encrypted_len + 8; apdu->lc = encrypted_len + 8; if (apdu->cse == SC_APDU_CASE_2_SHORT) apdu->cse = SC_APDU_CASE_4_SHORT; if (apdu->cse == SC_APDU_CASE_1) 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); } OpenSC-0.26.1/src/smm/sm-module.h000066400000000000000000000070161474147347300164070ustar00rootroot00000000000000/* * sm-module.h: Support for the external Secure Messaging module for * IAS/ECC and 'AuthentIC v3' cards * * Copyright (C) 2010 Viktor Tarasov * OpenTrust * * 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 _SM_MODULE_H #define _SM_MODULE_H #ifdef __cplusplus extern "C" { #endif #include #include #include "libopensc/sm.h" #include "sm/sm-common.h" /* Global Platform definitions */ 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, struct sc_remote_data *out, int (*diversify_keyset)(struct sc_context *ctx, struct sm_info *sm_info, unsigned char *idata, size_t idata_len)); int sm_gp_initialize(struct sc_context *ctx, struct sm_info *sm_info, struct sc_remote_data *out); int sm_gp_securize_apdu(struct sc_context *ctx, struct sm_info *sm_info, char *init_data, struct sc_apdu *apdu); int sm_gp_decode_card_answer(struct sc_context *ctx, struct sc_remote_data *rdata, unsigned char *out, size_t out_len); void sm_gp_close_session(struct sc_context *ctx, struct sm_gp_session *gp_session); /* CWA-14890 helper functions */ int sm_cwa_initialize(struct sc_context *ctx, struct sm_info *sm_info, struct sc_remote_data *out); int sm_cwa_get_apdus(struct sc_context *ctx, struct sm_info *sm_info, unsigned char *init_data, size_t init_len, struct sc_remote_data *out, int release_sm); int sm_cwa_decode_card_data(struct sc_context *ctx, struct sm_info *sm_info, char *str_data, unsigned char *out, size_t out_len); int sm_cwa_securize_apdu(struct sc_context *ctx, struct sm_info *sm_info, struct sc_remote_apdu *rapdu); 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); int sm_cwa_init_session_keys(struct sc_context *ctx, struct sm_cwa_session *session_data, unsigned char mechanism); /* SM AuthentIC v3 definitions */ int sm_authentic_get_apdus(struct sc_context *ctx, struct sm_info *sm_info, unsigned char *init_data, size_t init_len, struct sc_remote_data *out, int release_sm); /* SM IAS/ECC definitions */ int sm_iasecc_get_apdus(struct sc_context *ctx, struct sm_info *sm_info, unsigned char *init_data, size_t init_len, struct sc_remote_data *out, int release_sm); int sm_iasecc_decode_card_data(struct sc_context *ctx, struct sm_info *sm_info, struct sc_remote_data *rdata, unsigned char *out, size_t out_len); #ifdef __cplusplus } #endif #endif OpenSC-0.26.1/src/smm/smm-local.c000066400000000000000000000251351474147347300163660ustar00rootroot00000000000000/* * smm-local.c: Secure Messaging 'local' module * * Copyright (C) 2010 Viktor Tarasov * OpenTrust * * 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 #include #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libopensc/opensc.h" #include "libopensc/cards.h" #include "libopensc/log.h" #include "libopensc/iasecc.h" #include "sm-module.h" static int sm_gp_config_get_keyset(struct sc_context *ctx, struct sm_info *sm_info) { scconf_block *sm_conf_block = NULL, **blocks; struct sm_gp_keyset *gp_keyset = &sm_info->session.gp.gp_keyset; const char *kmc = NULL; unsigned char hex[48]; size_t hex_len = sizeof(hex); int rv, 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]; free(blocks); } if (sm_conf_block) break; } kmc = scconf_get_str(sm_conf_block, "kmc", NULL); if (!kmc) return SC_ERROR_SM_KEYSET_NOT_FOUND; rv = sc_hex_to_bin(kmc, hex, &hex_len); if (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_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 = (unsigned)hex_len; return SC_SUCCESS; } static int sm_cwa_config_get_keyset(struct sc_context *ctx, struct sm_info *sm_info) { struct sm_cwa_session *cwa_session = &sm_info->session.cwa; struct sm_cwa_keyset *cwa_keyset = &sm_info->session.cwa.cwa_keyset; scconf_block *sm_conf_block = NULL, **blocks; struct sc_crt *crt_at = &sm_info->session.cwa.params.crt_at; const char *value = NULL; char name[128]; unsigned char hex[48]; size_t hex_len = sizeof(hex); int rv, ii, ref = crt_at->refs[0] & IASECC_OBJECT_REF_MAX; 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]; free(blocks); } if (sm_conf_block) break; } 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", sc_dump_hex(sm_info->current_aid.value, sm_info->current_aid.len), ref); else snprintf(name, sizeof(name), "keyset_%02i_enc", ref); value = scconf_get_str(sm_conf_block, name, NULL); if (!value) { sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, "No %s value in OpenSC config", name); return SC_ERROR_SM_KEYSET_NOT_FOUND; } 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); } else { hex_len = sizeof(hex); rv = sc_hex_to_bin(value, hex, &hex_len); if (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_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_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)) snprintf(name, sizeof(name), "keyset_%s_%02i_mac", sc_dump_hex(sm_info->current_aid.value, sm_info->current_aid.len), ref); else snprintf(name, sizeof(name), "keyset_%02i_mac", ref); value = scconf_get_str(sm_conf_block, name, NULL); if (!value) { sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, "No %s value in OpenSC config", name); return SC_ERROR_SM_KEYSET_NOT_FOUND; } 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); } else { hex_len = sizeof(hex); rv = sc_hex_to_bin(value, hex, &hex_len); if (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_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_debug(ctx, SC_LOG_DEBUG_SM, "%s %s", name, sc_dump_hex(cwa_keyset->mac, 16)); cwa_keyset->sdo_reference = crt_at->refs[0]; /* IFD parameters */ //memset(cwa_session, 0, sizeof(struct sm_cwa_session)); value = scconf_get_str(sm_conf_block, "ifd_serial", NULL); if (!value) return SC_ERROR_SM_IFD_DATA_MISSING; hex_len = sizeof(hex); rv = sc_hex_to_bin(value, hex, &hex_len); if (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_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_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_debug(ctx, SC_LOG_DEBUG_VERBOSE, "Generate random error: %i", rv); return SC_ERROR_SM_RAND_FAILED; } 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; } /** API of the external SM module */ /** * Initialize * * Read keyset from the OpenSC configuration file, * get and return the APDU(s) to initialize SM session. */ int initialize(struct sc_context *ctx, struct sm_info *sm_info, struct sc_remote_data *out) { int rv = SC_ERROR_NOT_SUPPORTED; LOG_FUNC_CALLED(ctx); if (!sm_info) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); 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"); }; LOG_FUNC_RETURN(ctx, rv); } /** * Get APDU(s) * * Get securized APDU(s) corresponding * to the asked command. */ int get_apdus(struct sc_context *ctx, struct sm_info *sm_info, unsigned char *init_data, size_t init_len, struct sc_remote_data *out) { int rv = SC_ERROR_NOT_SUPPORTED; LOG_FUNC_CALLED(ctx); if (!sm_info) LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); 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); LOG_TEST_RET(ctx, rv, "SM get APDUs: failed for AuthentIC"); } else if (sm_info->card_type/10*10 == SC_CARD_TYPE_IASECC_BASE) { rv = sm_iasecc_get_apdus(ctx, sm_info, init_data, init_len, out, 1); LOG_TEST_RET(ctx, rv, "SM get APDUs: failed for IAS/ECC"); } else { LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "SM get APDUs: unsupported card type"); } LOG_FUNC_RETURN(ctx, rv); } /** * Finalize * * Decode card answer(s) */ int finalize(struct sc_context *ctx, struct sm_info *sm_info, struct sc_remote_data *rdata, unsigned char *out, size_t out_len) { int rv = SC_ERROR_INTERNAL; LOG_FUNC_CALLED(ctx); 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); if (sm_info->sm_type == SM_TYPE_GP_SCP01) rv = sm_gp_decode_card_answer(ctx, rdata, out, out_len); else if (sm_info->card_type/10*10 == SC_CARD_TYPE_IASECC_BASE) rv = sm_iasecc_decode_card_data(ctx, sm_info, rdata, out, out_len); else LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "SM finalize: cannot decode card response(s)"); LOG_FUNC_RETURN(ctx, rv); } /** * Module Init * * Module specific initialization */ int module_init(struct sc_context *ctx, char *data) { return SC_SUCCESS; } /** * Module CleanUp * * Module specific cleanup */ int module_cleanup(struct sc_context *ctx) { return SC_SUCCESS; } int test(struct sc_context *ctx, struct sm_info *info, char *out, size_t *out_len) { return SC_SUCCESS; } OpenSC-0.26.1/src/smm/smm-local.dll.manifest000066400000000000000000000010041474147347300205110ustar00rootroot00000000000000 OpenSC-0.26.1/src/smm/smm-local.exports000066400000000000000000000000761474147347300176450ustar00rootroot00000000000000initialize get_apdus finalize module_cleanup module_init test OpenSC-0.26.1/src/tests/000077500000000000000000000000001474147347300146765ustar00rootroot00000000000000OpenSC-0.26.1/src/tests/Makefile.am000066400000000000000000000020521474147347300167310ustar00rootroot00000000000000include $(top_srcdir)/win32/ltrc.inc MAINTAINERCLEANFILES = $(srcdir)/Makefile.in EXTRA_DIST = Makefile.mak SUBDIRS = regression p11test fuzzing unittests noinst_PROGRAMS = base64 lottery p15dump pintest prngtest AM_CPPFLAGS = -I$(top_srcdir)/src AM_CFLAGS = $(OPTIONAL_OPENSSL_CFLAGS) LIBS = \ $(top_builddir)/src/libopensc/libopensc.la \ $(top_builddir)/src/common/libscdl.la \ $(top_builddir)/src/common/libcompat.la COMMON_SRC = sc-test.c COMMON_INC = sc-test.h base64_SOURCES = base64.c $(COMMON_SRC) $(COMMON_INC) lottery_SOURCES = lottery.c $(COMMON_SRC) $(COMMON_INC) p15dump_SOURCES = p15dump.c print.c $(COMMON_SRC) $(COMMON_INC) pintest_SOURCES = pintest.c print.c $(COMMON_SRC) $(COMMON_INC) prngtest_SOURCES = prngtest.c $(COMMON_SRC) $(COMMON_INC) if WIN32 base64_SOURCES += $(top_builddir)/win32/versioninfo.rc lottery_SOURCES += $(top_builddir)/win32/versioninfo.rc p15dump_SOURCES += $(top_builddir)/win32/versioninfo.rc pintest_SOURCES += $(top_builddir)/win32/versioninfo.rc prngtest_SOURCES += $(top_builddir)/win32/versioninfo.rc endif OpenSC-0.26.1/src/tests/Makefile.mak000066400000000000000000000013351474147347300171070ustar00rootroot00000000000000TOPDIR = ..\.. 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 LIBS = $(TOPDIR)\src\common\common.lib $(TOPDIR)\src\libopensc\opensc.lib all: $(TARGETS) !INCLUDE $(TOPDIR)\win32\Make.rules.mak $(TARGETS): $(OBJECTS) $(LIBS) opensc-minidriver-test.exe: cl $(COPTS) /c $*.c link $(LINKFLAGS) /pdb:$*.pdb /out:$@ $*.obj bcrypt.lib ncrypt.lib crypt32.lib winscard.lib if EXIST $@.manifest mt -manifest $@.manifest -outputresource:$@;1 .c.exe: cl $(COPTS) /c $< link $(LINKFLAGS) /pdb:$*.pdb /out:$@ $*.obj $(OBJECTS) $(LIBS) if EXIST $@.manifest mt -manifest $@.manifest -outputresource:$@;1 OpenSC-0.26.1/src/tests/base64.c000066400000000000000000000017431474147347300161330ustar00rootroot00000000000000#include "config.h" #include #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"); goto err; } inf = fopen(argv[1], "r"); if (inf == NULL) { perror(argv[1]); goto err; } sz = fread(buf, 1, sizeof(buf), inf); if (sz < 0) { perror("fread"); goto err; } 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)); goto err; } fwrite(outbuf, len, 1, stdout); r = 0; err: if (inf) fclose(inf); return r; } OpenSC-0.26.1/src/tests/fuzzing/000077500000000000000000000000001474147347300163725ustar00rootroot00000000000000OpenSC-0.26.1/src/tests/fuzzing/Makefile.am000066400000000000000000000046431474147347300204350ustar00rootroot00000000000000AM_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 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 OpenSC-0.26.1/src/tests/fuzzing/README.md000066400000000000000000000222411474147347300176520ustar00rootroot00000000000000# 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 ``` ### 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 |` OpenSC-0.26.1/src/tests/fuzzing/corpus/000077500000000000000000000000001474147347300177055ustar00rootroot00000000000000OpenSC-0.26.1/src/tests/fuzzing/corpus/fuzz_card/000077500000000000000000000000001474147347300216745ustar00rootroot00000000000000OpenSC-0.26.1/src/tests/fuzzing/corpus/fuzz_card/3676fcfa2dba95b7c439b6343228623ac0be93c4000066400000000000000000000026301474147347300272570ustar00rootroot00000000000000;1E%VWSC6508mz0&@@GSNPBP)xcf`cJIK6d L@A83=/3/]!;R!-H ()nlz3#/VGw^FFnV/CuU6P.dԔ\Ң̒J\[}Dуe?elճV,js["7&~v2[M]w |o45\Y%b9_fm/YR\2gs1֮tä)1=)7,ez1991;.|k7ġ^$"o;yncFVipOZaĦ a!C%̸AY)&r$0Ud蕩, 3KLuťsllV?ݒInco[ꍦϋ|twb]ɡֿBɏzch1rIӖ_k';2VfgoƝSQz,ְzyTXPyg<w}Vص]wp1LŪ@t{÷-\qSpY(T2EGY'i7Žӻs*"= -Kk8{RfҲЫ3axp6HyxF&qLRr*ּe3*V{8OZ`b],AفUKbzow_s@{k=„Eo |tx: ^-* 3w%T%O8egmϐڿrS={'OݱbMN32Ll?}++zMA&`0S 7!e*fOpenSC-0.26.1/src/tests/fuzzing/corpus/fuzz_card/3e651eeafa4f5ad5bb6e21787ba4ba43af7c6f76000066400000000000000000000071021474147347300276400ustar00rootroot00000000000000P;}1emmmmmmmma o a o a4a46a ay@pPr6X,!!yyyyyyyy00`0`000yyaya l@QABRCa a qp]r&aax3hb7hb1A߀Sͣ;/##++A!'s( 0Sh%_ZWbcfvwAx{x|.. ^! Ύr&F`%khZZFSv/4P@ ? =_ OW=CƖab_e$;wd۰"Q[Ś/ ,}UN=vasɚf~|;{%7n 9;b>:,w?ް[xi|=]ƛ*62] 7Umő/wnxҾe-qנ@Ye`4  U&QrB&'z @ YltqaFII~rQ^^f^Jfq^nf~zj P?FR88 dX x 8<S9 dA|,ba,"\ag}7^٥pt:6m6`bgdd)2(0 ڌ83=۔ ˊQ29E@@Ő=s##\'9YIo]WjA)Fߋ94? 'zW?W0!.)SzϛN\*rk)VCwMctҹ-)B*B,yL-@a^/%8<*oq<}͕[h'J6.kCvVEx᭨ޱ=-Vys}qq=iq'*2W5U8@tVWVYb;7|N{b2758b1e-508a-1275-92da-98506be00000}a l@QABTCa a qpr&aax3hbaܰƀSͣ;/##++A!'s( 0Sh%_ZWbcfvwAx{Hx".. ^! > Ύr&F`%khZZFS ^hE!A~AzAz@[͍-- ##+sc/Ac'Sc#eHJ)-TxAnTo aXEg%.yhRU֬wr= hvH<)ӆ3y|ŋ֭3)gq6p4_`2J_?(G//D/%8Q/73G?=(f%U#P<)pLiH\gdv ,l maLܜ q>1*Go0}n9PѠC Y * `|6+<+L>=9mJPs ˊQ4SQ((/ȡ(5%#D/9?w8&s&<|Jײ,"B09Y Y@ @$ʢdd L3ł똛V$/O|v0ؾ~lgDqvMohHaRxN]+{,]f|NyIgW^|&v8NFޕqBg"Nd?lʦQ{_ۼkWm ?>to\̹~vI9\?Sf1h{쳋m T.ߓ2U69O2w_+C'gEnjNު[Ulsl{c75bbada-529a8-175b-92dc-98506be00000}a l@QABWCa a qpr&aax3hb5hb6][kƩ ɐۀ9M)4P@  Sp/K-M+11p 3 = e A=h$!l†>_ Y+G׸Ϫ/;.vgv6h&fFMM@*ATIO>$xu @ Y  .n())O.,K,NOO-cIO@ S@W| 03*BV߀׀3̓a1@$""F㝹&ӄQwlOg4hlcf0vFF* 7߀ 3OncԜ<b#!d3i-)-K-r(JMH,K[YN!ɜ,,UgNtmZɥX iL2'xx?讏x'6|0t?9mGS|ѬRb,7QU͋aNLiIx69NOm;̻{"{ 7^2)G.|+vt\U.qt}RS.C&69>;?JsǞ8=F}z-;r:sLtm4~1sr;+}75>!)gRZ J:]tNc{d9857516-0054-1985-92de-98506be00000}cOpenSC-0.26.1/src/tests/fuzzing/corpus/fuzz_card/68446db83043eb66ce144ab42466b39b0675c42d000066400000000000000000000061321474147347300270400ustar00rootroot00000000000000P;1Yubikey4jjjmmmaOyO ~O a _/@~O _/@ ~O a _/@~O _/@~O _/@j~O _/@ S;09a57s9shX!B!8B4SM%B)&8520300101>?S;09s9shX!B!8B4SM%B)&8520300101> SVpMa0I010  *H  0C10U Example10U Example Test10U Example Test CA0 190703094638Z 220329094638Z0310 U jjelen10 U jjelen10 U jjelen0Y0*H=*H=ḆZ\ qOӐSY^̟` 3W)m˿ naSVpM0I010  *H  0C10U Example10U Example Test10U Example Test CA0 190703094638Z 220329094638Z0310 U jjelen10 U jjelen10 U jjelen0Y0*H=*H=ḆZ\ qOӐSY^̟` 3W)ma˿ n){ja$rtF'ã#0!0U#0rv pZ{D<ӡ=0  *H  AۓʅHFE\_<ˈ4B7ٜ7HE-r==>cmUoDՓzQ|ylM|d^(M 9 fTF %GCt;+Ip݋P0Du?KI? fo҆$.B9D|a]_2˫ؽJcDSf}%AS+},AӉv< HuЪOdɹ4|~O4yD'L#+va "K[;8דqb,`G.CMQ#4H*O6DL=C'7yaSp00 ]ό_0  *H  010U SSH key0 171114115934Z 181114115934Z010U SSH key0"0  *H 0 ט>< HuЪOdɹ4|~O4yD'L#+va "K[;8דqb,`G.CMQ#4H*O6DL=Ca'7yhɿ6E mcȸ U.HuS0i GewjģVQ28Vk"#GC Pܰ@No(y*+b3VNKCʧo8"xkKUU+ZjThC,Ȕ ̂dA0  *H  q Slpca0_0 ;ˊ`0 *H=010U SSH test key0 190311081112Z 200310081112Z010U SSH test key0v0*H=+"b|)<éygp,d7/{ΚRUV&%^-ʠtim- x^TYYLQEv RN:R0 *H=h0eaiSlpc0_0 ;ˊ`0 *H=010U SSH test key0 190311081112Z 200310081112Z010U SSH test key0v0*H=+"b|)<éygp,d7/{ΚRUV&%^-ʠtim- x^TYYLQEv RN:R0 *H=arth0e0?jnFƃ˝YIlˤ\/(ηķMe,`1؈ Ju>F7Mub%^tm}i{èC>,1uq Sp0a00  *H 00 000001010000Z 000001010000Z000  *H 0{lh5,PS9/|EtNAR7 ;daR #!JCr#`[HI⊮<B8^AuR*> JxWHQ_TtTh\h0  *H qSp000  *H 00 000001010000Z 000001010000Z000  *H 0{lh5,PS9/|EtNAR7 ;daR #!JCr#`[HI⊮<B8^AuR*> JxWHQ_TtTh\h0  *H q~O _/@cOpenSC-0.26.1/src/tests/fuzzing/corpus/fuzz_card/995545e7be2e433f450b87c2e9020ab480947bcf000066400000000000000000000635321474147347300271430ustar00rootroot00000000000000@ ;1Xj*o%P1<> 0?PD 0?PDA 0?PDQ 0?PDq 0?PD*o%P2240*QP Volkswagen AG VW PKI Cardrd$oD 0F0 Card PIN0,0* 00000000000000Z0?0:0 Signature PIN00L 0?oDl/080 Digital Signature0 40 0?P0:0 Non Repudiation0 3@0 0?P0<0 Encryption0aD00 ?P0<0 Encryption0bD00 ?P0<0 Encryption0cD00 ?P0<0 Encryption0dD00 ?P0<0 Encryption0et00 ?PvoDA/0/0 Digital Signature@0 0 0?PEA0-0 Non Repudiation@03 0 0?PEC0/0 Encryption@0a00?PEt000 Encryption@0b00?PEt000 Encryption@0c00?PEt Ȁ000 Encryption@0d00?PEt000 Encryption@0e00?PEtoDQ/060@0225118030566055813900?PEtl080 @0468135259511316737500?PEt"q070 @0818677575340868483800?PEsi090 @0-103714765826740230900?PEsiioEA /00J0  *H  0\10 &,dcom10 &,dvwg10 &,dVWPKI10UVW-QS-CA-OTHR-010 161021074212Z 201021074212Z0G10 &,dvwg100.U'Musterfrau Maike VWPKI FB247F21CE25B7BE0"0  *H 0 qhڙ+h!#fN;_VGa]S, Ր0NJ} gyӡࢿ򮱓8e<3S="SԈpZ=hr 5 9F UCЙ˹O4k4Kkѓ<2=8^rh)Т-y[v.fhtoJ쬋 3~L:#@n[ ݻA2Y5 w # {00PUI0G%extern.maike.musterfrau@volkswagen.de +7 VWMUSTE@vw.vwg03U%,0*+++ +70U# 0 K%Q+:'r0H+<0:08+0,http://qocsp.volkswagen.de/VW-QS-CA-OTHR-01/0U 00+ [0U0031/-http://crl.volkswagen.de/VW-QS-CA-OTHR-01.CRL0JHFDldap://ldap.volkswagen.de/cn=VW-QS-CA-OTHR-01,dc=vwPKI,dc=vwg,dc=com0U Bf-*0U0*p" 11511250 U00  *H  t~ +Fy4Y{85`y>E{C)-a?]eTJY͏mcxFA82k쌧seKY] sG,pm8 [Y !껆K[zOP_!R`660mLj)zH֋/2KC` 钔T_ѹz?tՐ3 $qk&ṁNG-/uNX߿MxэgH!{5+XֵX̽iz(*8_"!im-[S2膇O$s9s qwX )T(UEz:@YxM66м鑍:`ZV611$റL8Q՞+n<3&1??!yC0Ry=v]rum] *C^O^EsZ0H+<0:08+0,http://qocsp.volkswagen.de/VW-QS-CA-ENCS-01/0U 00+ [ 0U0031/-http://crl.volkswagen.de/VW-QS-CA-ENCS-01.CRL0JHFDldap://ldap.volkswagen.de/cn=VW-QS-CA-ENCS-01,dc=vwPKI,dc=vwg,dc=com0U L܂*0U000U)0'%extern.maike.musterfrau@volkswagen.de0*p" 11511250 U00  *H  UTk+uq#EJhAp8 ZL d=2Uƍք9(A펺LOIG_YE̻:\Zt+*d:Ҷm:")Sna]#'Ə"(l9dElGAb?oŠg#@1אlW]݈]MUF\`'H{/ŒۢE |By_\Jz{EAsDWnoD ^@B#\ n8F{b=݁q(fҌQ!͵{F}walftmեGq/n`0A:4"T 4`c4KtjCH4{Ȅ~EgA؄$p܀T5-H~dy^-Eb&WLC33&e+X6(@7<_\3vŘMK8Ip;ns@{*4"zKMiz: zQT*o%Et*00ȠI0  *H  0\10 &,dcom10 &,dvwg10 &,dVWPKI10UVW-QS-CA-OTHR-010 160609131733Z 200609131733Z0G10 &,dvwg100.U'Musterfrau Maike VWPKI FB247F21CE25B7BE0"0  *H 0 O~\MƴJK}!RJӇj 9my]y_Щ-T/;ۈ74$Gjcf-I@dOq̡3,a^UGMexK)Г2й䪆]"gwzcP4a9px5UۥC>:rL̴ 柇gߥ2JnH\ \IJ{L zwCD=ȑ8\{C FY[W^{R000U)0'%extern.maike.musterfrau@volkswagen.de06U%/0-++ +7  +7 0U# 0 K%Q+:'r0H+<0:08+0,http://qocsp.volkswagen.de/VW-QS-CA-OTHR-01/0U 00+ [0U0031/-http://crl.volkswagen.de/VW-QS-CA-OTHR-01.CRL0JHFDldap://ldap.volkswagen.de/cn=VW-QS-CA-OTHR-01,dc=vwPKI,dc=vwg,dc=com0U ID_0U00*p" 11515880 U00  *H  ]GlTC_|µXL0=]aGcL;3P?J~](f@[)ޙCc ϡɬ:W]a0F'KJE;ݑWBGHqnd#8݌0r`=)"dxl9M}4*֫I5zF"]٥h$z[!\` p՜MPcq ,*p2ɕqUBOQ[1-@红{Dɛ ^fFk97lKACwx}τ2ٺ`u5!``dGGK:wTT9ފ iI;X{\4yڒYJ5S__(8}\eeBΧO qpz5,p*o%Et*00ˠI-0  *H  0\10 &,dcom10 &,dvwg10 &,dVWPKI10UVW-QS-CA-OTHR-010 160519142351Z 200519142351Z0G10 &,dvwg100.U'Musterfrau Maike VWPKI FB247F21CE25B7BE0"0  *H 0 M ћa&Gi)b6~)` X#ѯ'.Bg}țF{4doҬJqӏw>n*V{VצgNJczpVHSk>jAuw~vx[۳r) o=̞1;)j Xg"ٳU, Zn3j Mq0Ye7?0#'{=&y4#000U)0'%extern.maike.musterfrau@volkswagen.de06U%/0-++ +7  +7 0U# 0 K%Q+:'r0H+<0:08+0,http://qocsp.volkswagen.de/VW-QS-CA-OTHR-01/0U 00+ [0U0031/-http://crl.volkswagen.de/VW-QS-CA-OTHR-01.CRL0JHFDldap://ldap.volkswagen.de/cn=VW-QS-CA-OTHR-01,dc=vwPKI,dc=vwg,dc=com0U MP5 80U00*p"  10550003820 U00  *H  an{Hx/.K07mwW) x3bQ`TV2mxEuz\f W@v)VA\ϵ#]000U)0'%extern.maike.musterfrau@volkswagen.de06U%/0-++ +7  +7 0U# 0 K%Q+:'r0H+<0:08+0,http://qocsp.volkswagen.de/VW-QS-CA-OTHR-01/0U 00+ [0U0031/-http://crl.volkswagen.de/VW-QS-CA-OTHR-01.CRL0JHFDldap://ldap.volkswagen.de/cn=VW-QS-CA-OTHR-01,dc=vwPKI,dc=vwg,dc=com0U LbU10U00*p" 11504650 U00  *H  5cy7@_ v:E$l15-L#E}k?.KoIK&҅* =hJC|j`LW%c *6ydP4EkHgʥŒ1k| }qB[''r:BlOw5+MO8dq `JOeĿ& лu9xahŤZT {K9FBlTM-+2Ґ4-ʹhL}+?4U.gJEЇ (d]N_ 6`GEKtS r+5vfJ>УD' fSkWeٮN-QaPUv(zDL9Ul^iZ0ZX٫KB5޷tg\ӴfkrFYcMF_D/.>T TAy.̕'n`n 17/{ ']:0X:Zġ!bi641dTo<N4BK_' C00@U%907+++ +7  +7 0U# 0 KFql0H+<0:08+0,http://qocsp.volkswagen.de/VW-QS-CA-PROC-01/0U 00+ [0U0031/-http://crl.volkswagen.de/VW-QS-CA-PROC-01.CRL0JHFDldap://ldap.volkswagen.de/cn=VW-QS-CA-PROC-01,dc=vwPKI,dc=vwg,dc=com0U Cn0U00U)0'%extern.maike.musterfrau@volkswagen.de0 U00  *H  4}u1Xl;- +Nʿ|kdHO"9a3G:fܘ-"k(qH;12`iZ5|28:7DUl i7O-;f_9u3GKqbE+dAv=a* zY㤧pTѥ*ߕ7v_ak?3]$h؇etjČaيQ<5Xa7igCXHCL5z^ro|);~܍@!dH FEv%aԗ:8t!JR$tZ$U`*]'Ͽzfje(!~7Lp ,JݝQKW#1\7ERG|?5J^ ËQ :o!dF1 AY~Z. Cm?QE8S#?<[}咒+wO-RPcbP`>cJu|ي-EݜRuOy<5\?,_VJ8$:3]h\!G.?O$$k#kΗ}r̦!u0t#gH"P QL_2eJES0Q0U00U GY m}0U 00+ [0 U0  *H  E{Ri6hr%(/!w{M$s“r hܧPzg^R~&Pg8REk>W* fuF񳀓5moK I $wDZ*rWɼyp4jiVG'+ w9 - !_E]aPH⥢&AHQud$Q<¹Cu-uB*BvcK;ߴU(0GWT_nŐ],c[疢l҂Emf Vΰ+5;G_=^x:n˗%OH dm' wtHǛ494ʈ±ثeUq!wIfPEa4'0,L@ ~\f)+#p,g{*o%Et*0m0UB'0  *H  0\10 &,dcom10 &,dvwg10 &,dVWPKI10UVW-QS-CA-ROOT-010 130606100000Z 210806100000Z0\10 &,dcom10 &,dvwg10 &,dVWPKI10UVW-QS-CA-OTHR-010 0  *H  0Y9D8;m ĪU_}BݼWFyA Hf@NS7o^>u9iӫ 8O ),UֳA\ w+1z6^1x ]Cpip~>=PFD5f>la{h_*)z N =yX6J>%2OA ;Lz+döQ֛@ĊiL gm::Rۤ#߹fC7su{ܶ@v9B%JƤV4感ZSƶF*1nr% ޓ!QdfBw՘e#(~yhGX6ܥtɃ` 5ێOb5qo NsQ%EŎW~&E/B@tQL $@yh=WjL`hQ? [ BnSH2Ƀ oϝqTA9050U00U K%Q+:'r0&U 00U 0+ [0U# 0 GY m}06+*0(0&+0http://qocsp.volkswagen.de0 U0U0031/-http://crl.volkswagen.de/VW-QS-CA-ROOT-01.CRL0JHFDldap://ldap.volkswagen.de/cn=VW-QS-CA-ROOT-01,dc=vwPKI,dc=vwg,dc=com0  *H  Oa.V?brbX gx5-7b_mToЫjG@{3iҗ7lStOflH, 4QP7M~%+)*Ɣ8V)^*%&'rOUOz0L ˢS7>L+rRTIML9 vMS&S_!@zbrG^*5P ; o;8:Q(4O'o8q{g6BsӫuۭUyfFr ɭ  3?~o,srۮOw;f$Y}ǜd j9+~Na]BU(E{N?a_U-;c~tP]f3FTt,P dمVS5]@ѧ@-h9}vRh|,kk՟BC%C$O*{od J#&*o%Es*0e0MB%0  *H  0\10 &,dcom10 &,dvwg10 &,dVWPKI10UVW-QS-CA-ROOT-010 130606100000Z 210806100000Z0\10 &,dcom10 &,dvwg10 &,dVWPKI10UVW-QS-CA-ENCS-010 0  *H  0ږfVgX^a 䈤9rJO/qRFWTjπp B R7sqS/ϕw:]"_E~=(ݐR `g"wA}v2U{ -Ǒ㖈'ϙYmP\vd⼟Nϣ,$v9;b#鉌'bt?<΅ 'S\9yg^TKe/LASM'h,@-)}oTη"8maȔo@C=_ͣrC<kN^gTcv̎1D1ӠEnacKSFmF!n#RHn|]N#| P꾷7/)Sغ=165RewFAeb5* 3:cwټ:`VquAO:h!_?SB852Rlz{h;$&0z}9|0.mS#% lrcO&\ &J5SB9W10-0U00U L%>sZ0U 00+ [0U# 0 GY m}06+*0(0&+0http://qocsp.volkswagen.de0 U0U0031/-http://crl.volkswagen.de/VW-QS-CA-ROOT-01.CRL0JHFDldap://ldap.volkswagen.de/cn=VW-QS-CA-ROOT-01,dc=vwPKI,dc=vwg,dc=com0  *H  v/(bj$oP JcC{gzӑ\U?17; 3:P0L_4 8dE\yMFȓG Uv2 \LyKx(rZOY_)YE4>VMQcjR%d}-G|$OПb>([L@NɍnФ=IB/#5zTqeOVtÁLF<1UK2X ID-93QHjXx,VYM5WN%նO,Z]u50@{W,|Dֲ΄_WeېMd@ZѬ; H򊇐s|*r[3 k(%IdYҐHRvU;afn'k-⦏e 1z!qًH6 ]vR}C)&zCsl"+>*o%Es*0e0MI0  *H  0\10 &,dcom10 &,dvwg10 &,dVWPKI10UVW-QS-CA-ROOT-010 160606100000Z 210806100000Z0\10 &,dcom10 &,dvwg10 &,dVWPKI10UVW-QS-CA-SIGN-020 0  *H  0F+'D!Gh</'_J3ҵf/krC |4G:ur$CT1ANhݒP~7Xti\}G"„*c51νB7.X$s%E ;ScBYa Y!ɩ<|f`_ڗ{* #v/2*H'e=y$û釐Wdwd7C܊cw)%t>$& b4(Uf ōvJ4@I6|k{c $td|*VWb[wld$3iI;U}MY%-[1WGxNR͸+%T ~0a}/b{FrMf2. [}.'10-0U00U DTlVD0U 00+ [0U# 0 GY m}06+*0(0&+0http://qocsp.volkswagen.de0 U0U0031/-http://crl.volkswagen.de/VW-QS-CA-ROOT-01.CRL0JHFDldap://ldap.volkswagen.de/cn=VW-QS-CA-ROOT-01,dc=vwPKI,dc=vwg,dc=com0  *H  +L~ˠ- IU񫷞-J0c(e ؏@aVÜArDg ,lH(`# ]jUx@и$}rpz?LA!J@\9LKO:$ X+,5Ucwa=OO}ONLKe=WKseI:_ Ip=}uQtפX\*065XFb~諸{jc@J:;c8H[F)I'Q*Ə/TS(XzNG4bi0 *H 010  `He0 `He 000  *H  0r1 0 UUS10U Test Certificates 201010U Test CA100.U'Test RSA 2048-bit CA for Test PIV aS 0PX홒j١'DPC4520301231>0 *H 010  `He0 `He 000  *H  0r1 0 UUS10U Test Certificates 201010U Test CA100.U'Test RSA 2048-bit CA for Taest PIV Cards0 101001083000Z 301001083000Z0e1 0 UUS10U Test Government10U Test Department1"0 UTest PIV Content Signer 50"0  *H 0 ޷vIsS~1SasL%04dZ,$smt9Y,4p\Ta[Z;5JZ:( J % };5•_l$ N"_M(=AQEnmB;e 39%œ> U#؜4|iɰ=e`+S7ډ A44kܒ ufʢB֕$]اD y.T4һ00D+6020G+0;http://smime2.nist.gov/PIVaTest/CACertsIssuedToRSA2048CA.p7c0+0ldap://smime2.nist.gov/cn=Test%20RSA%202048-bit%20CA%20for%20Test%20PIV%20Cards,ou=Test%20CA,o=Test%20Certificates%202010,c=US?cACertificate;binary,crossCertificatePair;binary0(+0http://seclab7.ncsal.nist.gov0U00֠ӠІ,http://smime2.nist.gov/PIVTest/RSA2048CA.crlldap://smime2.nist.gov/cn=Test%20RSA%202048-bit%20CA%20for%20Test%20PIV%20Cards,ou=Test%20CA,o=Test%20Certificates%202010,c=US?certificateRevocationList;binary0U_aƾ+8l0U#0HDtW 0U 00  `He0U% 0 `He0U0  *H  Z Hd&QG2wK) ⡎ x ?Sr)cYdj XWIcΆC~"rC: hZÇR*oQ苵1#g xڸ,o9"sՍ@5eajfva$I팵cAYWkU׋;dʎW;NY5RGRhجMXN"r:;y0Kz1,`|̻.#cͮSI gT|10~0w0r1 0 UUS10U Test Certificates 201010U Test CA100.U'Test RSA 2048-bit CA for Test PIV Caards0  `He0 *H  1 `He0 *H  1 110403195841Z0/ *H  1" A.m6uv{3Fu0s`He1g0e1 0 UUS10U Test Government10U Test Department1"0 UTest PIV Content Signer 50  *H a Bj /UDr5 ~ZqbM ,D䲢YO` ۻ-K:ǫ45')XeN'R’ f$mhȈ;S%ŝE{K>WLSXEhACR=5 U/WaeXt Mb AGņXNK-9ga G"r" Spa3hbgڶҀSͣ;/##7+A!'s( 0Sh,%,Z\ZTXZ`d`h`(`R-Qhh`aV;UX&ea ΉE)r@ JkP& 1痥d2.E%`! 7X1=5/P@ TaのII-R 3hbTBFV&F~8S##1JaSp3hbgڶҀSͣ;/##7+A!'s( 0Sh,%,Z\ZTXZ`d`h`(`R-Qhh`aV;UX&ea ΉE)r@ JkP& 1痥d2.E%`! 7X1=5/P@ TaのII-R 3hbTBFV&F~8Sa##1JUj;/,ܐܧkk5;{]^S=K̗/lnfv}ԒԓdUuԣR3\%};(Ӓ\[},׶ۇ'>qiR|2rˏwڶӭ]_J ֮~4b}r44^Hsڃ#˝ߋmi~759ߗ'vȘ#8#ˁnu*Na A҅f21320.nb4hb652 0 Dylkӄ%gÏdXXDV]ԷYgf7RH9>Ӡ H^AMk //hЦQRR`_jY\_L=8&/PrvK.ikMyr-HPH J U4$J:0Ύ:p6R>fhllJ-ҙy>@WX'e%U 6#++;#a#KP&ffMΎ ۋ=KSSBP`lиɤHRI./.FH,m2pHMIL2K.΁{\32gaafbo0PUY d`F03qY,M"LLl,@iq2`eƕ , y 8!<6NFf`6H)d6\d9c , -M ,LJ R+s rRsq@ c[ "y-@"b朷3']usY-.m_ _faPҲY-e-ul_8~jp/:\Q 6ONw* YOOR^n§%gf3QDr'/o\H>u^@jݟ{6ye߸leKWO`ꏻl~Xt0{S+g?qJIV)Wf=|w/O]1ǥܙ2=''f>RIH_6gW3gz6 .3 y'1fVgd[)εJؠ H^AMk //hЦQRR`_jY\_L=8&/PrvK.ikMyr-HPH J U4$J:0Ύ:p6R>fhllJ-ҙy>@WX'e%U 6#++;#a#KP&ffMΎ ۋ=KSSBP`lиɤHRI./.FH,m2pHMIL2K.΁{\32gaafb;` +#66T`n dQ3PiTD/ Mr3sR+s rRsъCfP>wgȘė ߾A'/Eؤ(i;Tk(~U_6_۽%i#y+Vxhߛ+cUW)Gm5ax&[Z-D ߇O~aۓ#u>;iN1.Y0-XEK]+p.~x![ԃ/ \Ohx^O-L>uDc}oX+gҾ$P}Љw,ۛO q+y϶8̚@bWWX6߈^8gc5ҹ 4q Spa3hb30hbXZcƩȐۀ9M)4P@ I-.QpN-*LLN,I-V020400)f(p4400 E*M,t2A iE `0Ģb9q^I@dal``%k5(EuM`sRrSJ`2PԂĢD,㘞\i(c q0@d礤)Dx41*!#+s#?PᶾaSp3hb30hbXZcƩȐۀ9M)4P@ I-.QpN-*LLN,I-V020400)f(p4400 E*M,t2A iE `0Ģb9q^I@dal``%k5(EuM`sRrSJ`2PԂĢD,㘞\i(c q0@d礤)Dx41*!#+s#?Paᶾ7[מ|hľ-r?Տ[+uWe0CkWEP 0qҳ[_Xx{f~l~Cc١L`ذ} h[_CHS+"5+1SLX}'sו Y44h m:%%VŹFzy%ze3}`.gG䢜9)ؔ'ق5IX2A|`P%@A⠴_ 쨓g#i:ɶԲ| tuRf^bQ%0qh132a41E abLm|س45%${Lj\?С$ b$"Xh]& $s,?#f&Vq_Xq%y08fv] J}%z\nh␛Z[V29;Ą{/_U佥-8P?e@rl\߂o~tXKزԝgj>-Se^dƵNbO!bAEag3$}zwuզDŀ;/'y6חB:)MNsOR>vW'<5Inۂ/rK'y;7ݛyϜGel1x85&~_,҇o^??]|k>--"K[k7m^5;j u4q Spa3hb7hbM_ZeƩȐۀ9M)4P@ I-.QpN-*LLN,I-V020400)f(p4400 E*M,t2A iE `0Ģb9q^I@dal``%k5h( 1痥d2.E%`! 7X1=5/$*lbfj`hajaii$ntq44stt4t62t011aSp3hb7hbM_ZeƩȐۀ9M)4P@ I-.QpN-*LLN,I-V020400)f(p4400 E*M,t2A iE `0Ģb9q^I@dal``%k5h( 1痥d2.E%`! 7X1=5/$*lbfj`hajaii$ntq44stt4t6a2t01131v35hbTB=FV&F~8S##% ?7&+Xᶌ?qqp:8͎+}9XZON>.X٭f/;̞dEKA{ }͟'\̳y3Gr^U+Ek{t@2߫KONV_sz_Wc8,%^aUaдjOg9k*ne[aOU]T$=1ߍG׷m,쎮)mjs,6߼yDoC&fFML ZVC "<\iĒNh|,b,"}osJ' 6h|gicxӠڂ /dXfeS(r$bSg Rjd$a)tHBҮN~)LN>O$ۆ'#a$Rtf~IyEơh12p0Y#bԔ|D'4A2q@#4t򋋑3`w=c ,Rss^JYX催*cƑa W`coM,DSfƿwIe2^4ؘRÉLEZ77|Acz=\'Z"euew}>j6ۓWDSXCaƇGD,1V2۠wbƊW%}[f&z*e]0_/ _k]⥮"S̅׳Jemk>]9pr)~|w"Ma q Spa3hb30hbXhƩȐۀ9M)4P@ I-.QpN-*LLN,I-V020400)f(p4400 E*M,t2A iE `0Ģb9q^KcCKS3(q^C#$A)$ n_ZWʸ$eD 2`ԼJC)S!&#?'%H!3̠Q 9tY\LM [aSp3hb30hbXhƩȐۀ9M)4P@ I-.QpN-*LLN,I-V020400)f(p4400 E*M,t2A iE `0Ģb9q^KcCKS3(q^C#$A)$ n_ZWʸ$eD 2`ԼJC)S!&#?'%H!3̠Q 9tYa\LM [.d]g-c,e[]N"#JƜ";Zz*Ȭ&{R% |/(s?ϭ[){UT?a\힆՝3Q|Q3ՐГ]`nj, 5D>,Y_3[װؗٽ5wAKU?:[nKyw6FR.U) _d/$ۆ'#$Rtf~IyEơka,Mf@#wS5;;l/,..MM G@yA$th8"IAC@'(I< 1:p3!59'1\//8p#U@W`G}k_uMz/ً %a+OU7표Qw–oN^֏]vJEa^Y9{e8yj +X|GD<>%r㟻a͏7IM^Ƚk,FkꥁO]!YO9W'>1Nl<{@J9#. ?C3#7r6~=r]oڜ䴾ɹ.G _7~iÊ?S4q~O _/@cOpenSC-0.26.1/src/tests/fuzzing/corpus/fuzz_card/f117a2bbb1ea0617255c7e993914ef9062303580000066400000000000000000000051441474147347300267540ustar00rootroot00000000000000@;1ESCE7  FBjjjjmmjjjjo ymo yd Ay Py `y By Cy DyE CID_o y@ QAB C QAB C QAB C QAB C QAB C QAB C QAB C QAB C QA B C o y@ QA B C QA B C QA B C QA B C QAB C QAB C QAB C RAB DRAB Do y@ QA B C QA B C QA B C QA B C QAB C QAB C QAB C RAB DRAB Dr(qp,gd{EC65ECE3-9D4F-46DC-8B78-07541319A0E1}x3hb`|c3#S/VGw^FFnV$CnN6P6a`C 1K?T/XO=,(/75Đǀ $- 45E\] ;*l6ga)=ٟt/czͣ^;!UQy?~,6>kOzqIM>ˣ,7-]&4ڛ͎PpjY߈O·56=QOWThu>O(he7EL~dCAK/͊wŎERxdgx:^+eӐsvFn}rN"}CSf6Hm-NժM_R71Ty`P*AyO7)u/ޛw|KIY>1U>_nUf{cZ6m6FVVvFFB|[߀0J_?(G//D/%8Q/73G83=O@%81\/9@M =?@/8@($>03VE@fPb1I 886gMjNbf^^:0!%9&f怌^ d.ļ@@Vҳ 7gBc*##H\ ,0j3 @,PA6GC7n'ϖL_jJs.XP (O>{=x]V_G},M +y]]  64iG[}b+Ɛ2 ˵ՊzjJKcƢSSkBz͜:aN;I'n3c߾krϫ}U:כ5'ola_UUΥgX5,[_~m /ۖyE+Y{T[Yvxkb```b&&F Il̡,lLb 0^{~YjQ^nj^!H[%i(k 1 (xy8+x(+8;3220T*]_Hs܋R+R, ,169CSJynZ#3P!e:Dxcbd`P````?x=S'ܴxGf3rC26;9+t T#Ez^yzf .@c*l̡,lLb 0^{~YjQ^nj^!H[%i(l q99:HE ^=۴^-:vւɊb|,w|UشmªSz:.?-^DG0vC~Xl| |OpenSC-0.26.1/src/tests/fuzzing/corpus/fuzz_piv_tool/df81168351db4e248a9a915bb521c85dce1d17c8000066400000000000000000000057501474147347300302110ustar00rootroot00000000000000--serial;}1eփa~O _/@a~O _/@aSaSa~O _/@a~O _/@a~O _/@aSaSa~O _/@ja~O _/@aS 209s9shYfyP74Q"A_^c520301231>0 *H 010  `He0 `He,0(00  *H  0x1 0 UUS10U Test Certificates 201010U Test CA1604U-Test PIV-I RSA 2048-bit CAaaS 209s9shYfyP74Q"A_^c520301231>0 *H 010  `He0 `He,0(00  *H  0x1 0 UUS10U Test Certificates 201010U Test CA1604U-Test PIV-I RSA 2048-bit CAa for Test PIV Cards0 101001083000Z 301001083000Z0g1 0 UUS10U Test Government10U Test Department1$0"UTest PIV-I Content Signer 10"0  *H 0 M/= @lCvu0sObhcov4<5%O!AT4V TCaFlcF+‘ yKn3Zo#TU_*sՅ?ggP^ͻ1(¶:^ܿ*% h J7'ЗdA& M$YldS46{27O91TGydoMApAbyuJtOsw00P+B0>0K+0?http://smime2.nista.gov/PIVTest/CACertsIssuedToRSA2048PIVICA.p7c0+0ldap://smime2.nist.gov/cn=Test%20PIV-I%20RSA%202048-bit%20CA%20for%20Test%20PIV%20Cards,ou=Test%20CA,o=Test%20Certificates%202010,c=US?cACertificate;binary,crossCertificatePair;binary0(+0ahttp://seclab7.ncsl.nist.gov0U00⠁ߠ܆0http://smime2.nist.gov/PIVTest/RSA2048PIVICA.crlldap://smime2.nist.gov/cn=Test%20PIV-I%20RSA%202048-bit%20CA%20for%20Test%20PIV%20Cards,ou=Test%20CA,o=Test%20Certificates%202010,c=US?certificateRevoacationList;binary0U'xoT;?7#j S*p!aS*p!00ĠZ\K‹^ q;0 *H=01 0 U user0 220410173930Z 240201000000Z01 0 U user0Y0*H=*H=BG>p}kj}7 o̺HE2ӴA|^&2n?%@0 *H=H0E!/)N5(llV?gOa.0  7̭U QXw #su7gq Sfp]aSfp]0Y0VɃ:;z[o?œ|0 *H=01 0 U user0 220410174126Z 240420000000Z01 0 U user0v0*H=+"bTfll <3SdV/l80RgN^ŷzbCn# e6e̫,lVj5aVӏ\b50 *H=g0ajld0.1"vpÓJ<}rߚ6ix2mb'O0eqq–da|~A{V!rj>=cqj~O _/@OpenSC-0.26.1/src/tests/fuzzing/corpus/fuzz_pkcs11/334176efba3f10cbbb96b23d04ae03240ed31e0a000066400000000000000000000037461474147347300275520ustar00rootroot00000000000000A1111110 :CNAhu:q-pH#V@_.T5(U5%Ѽ<zEkZk9w1,IbNNMm dQf';1s!WYubiKey@jjjmmmaOyO ~O a ~O _/@ ~O a ~O _/@~O _/@j~O _/@ S;09a5?S;09s9shX!B!B4phV2Hl520300101>j S*p!aS*p!00ĠZ\K‹^ q;0 *H=01 0 U user0 220410173930Z 240201000000Z01 0 U user0Y0*H=*H=BG>p}kj}7 o̺HE2ӴA|^&2n?%@0 *H=H0E!/)N5(llV?gOa.0  7̭U QXw #su7gq Sfp]aSfp]0Y0VɃ:;z[o?œ|0 *H=01 0 U user0 220410174126Z 240420000000Z01 0 U user0v0*H=+"bTfll <3SdV/l80RgN^ŷzbCn# e6e̫,lVj5aVӏ\b50 *H=g0ajld0.1"vpÓJ<}rߚ6ix2mb'O0eqq–da|~A{V!rj>=cq SpaSp00z Entg$xn0  *H  01 0 U user0 220418192333Z 230630000000Z01 0 U user0"0  *H 0 @,W/<`ĸ10Lm23VH'a"+-z|Ov܏h<L4ʁ#e,*7-,RCRUx2xa ; E|^Bi\?1nM|Ez_xţД}7WEE;<88~C`|f>f[mřZ\e|>Svp% 9RϦ mGˤi\c(1on'o{bqI0  *H  t aKd¾NJ 2R3y[do1 I;;\BiO? x7aа=ww&D/#2=3\1mp\z!&V:v~K.\/~)Fב!.J7ڝ)i>V# +6,~SѶ@p] C>i mv`a *ݍ`wa(cSt\uO&||q~O _/@c~O _/@~O _/@L|HF0D _3KqBHߊ@އ;_tL GB+ϊAAB7!,W;AOpenSC-0.26.1/src/tests/fuzzing/corpus/fuzz_pkcs11/533432db786d023c678187d79db1860ca1c44056000066400000000000000000000034721474147347300271010ustar00rootroot00000000000000 v{73O} azmhH{NR7%˟!WƬ:zU@&,%, #l;1s!WYubiKey@jjjmmmaOyO ~O a ~O _/@ ~O a ~O _/@~O _/@j~O _/@ S;09a5?S;09s9shX!B!B4[-,`[T520300101> SpaSp00C {Ȕ KAon0  *H  01 0 U user0 220410173841Z 241001000000Z01 0 U user0"0  *H 0 6xi8S?*PueWE9i.|HE,Ln 랙;v.*`N,=er 6puas/[p&MŁpA Auتd +ܲVIRgUx@rB+e$Fo9?Uu IIÚ UǪl{3_;c&4cV~6M!γOͅR]bY0  *H  ?"G3>G |P3)iԹfb%9e7 %dQ0 -jkaS~ԓZ%S֧h+˖h:yٛk+jif 3ݭii8SCbܲF^R6 Nϧp'>}o0id( `{*+9h֮\:6*S=7 3_JR; /p7ܣ߈aղ q S*p!aS*p!00ĠZ\K‹^ q;0 *H=01 0 U user0 220410173930Z 240201000000Z01 0 U user0Y0*H=*H=BG>p}kj}7 o̺HE2ӴA|^&2n?%@0 *H=H0E!/)N5(llV?gOa.0  7̭U QXw #su7gq Sfp]aSfp]0Y0VɃ:;z[o?œ|0 *H=01 0 U user0 220410174126Z 240420000000Z01 0 U user0v0*H=+"bTfll <3SdV/l80RgN^ŷzbCn# e6e̫,lVj5aVӏ\b50 *H=g0ajld0.1"vpÓJ<}rߚ6ix2mb'O0eqq–da|~A{V!rj>=cqjOpenSC-0.26.1/src/tests/fuzzing/corpus/fuzz_pkcs11/6ec181a01600525601900b9fe2b9eacf7d5df43d000066400000000000000000000033511474147347300274340ustar00rootroot00000000000000 ;1s!WYubiKey@jjjmmmaOyO ~O a ~O _/@ ~O a ~O _/@~O _/@j~O _/@ S;09a5?S;09s9shX!B!B4[-,`[T520300101> SpaSp00C {Ȕ KAon0  *H  01 0 U user0 220410173841Z 241001000000Z01 0 U user0"0  *H 0 6xi8S?*PueWE9i.|HE,Ln 랙;v.*`N,=er 6puas/[p&MŁpA Auتd +ܲVIRgUx@rB+e$Fo9?Uu IIÚ UǪl{3_;c&4cV~6M!γOͅR]bY0  *H  ?"G3>G |P3)iԹfb%9e7 %dQ0 -jkaS~ԓZ%S֧h+˖h:yٛk+jif 3ݭii8SCbܲF^R6 Nϧp'>}o0id( `{*+9h֮\:6*S=7 3_JR; /p7ܣ߈aղ q S*p!aS*p!00ĠZ\K‹^ q;0 *H=01 0 U user0 220410173930Z 240201000000Z01 0 U user0Y0*H=*H=BG>p}kj}7 o̺HE2ӴA|^&2n?%@0 *H=H0E!/)N5(llV?gOa.0  7̭U QXw #su7gq Sfp]aSfp]0Y0VɃ:;z[o?œ|0 *H=01 0 U user0 220410174126Z 240420000000Z01 0 U user0v0*H=+"bTfll <3SdV/l80RgN^ŷzbCn# e6e̫,lVj5aVӏ\b50 *H=g0ajld0.1"vpÓJ<}rߚ6ix2mb'O0eqq–da|~A{V!rj>=cqj~O _/@| UJ| (&$OpenSC-0.26.1/src/tests/fuzzing/corpus/fuzz_pkcs11/85bdf4bb93d2f4604fa3e21096d7da552cae8b97000066400000000000000000000022611474147347300276170ustar00rootroot00000000000000 a111111 GЉ욢԰Wi/2 %L z(n5G2B%=݅2*aqM eH$. Ŷ͝Rba+d '6(QT9Hj\s% %On(3Or+;1s!WYubiKey@jjjmmmaOyO ~O a ~O _/@ ~O a ~O _/@~O _/@j~O _/@ S;09a5?S;09s9shX!B!B4y2=X ?{2520300101>j S*p!aS*p!00ĠZ\K‹^ q;0 *H=01 0 U user0 220410173930Z 240201000000Z01 0 U user0Y0*H=*H=BG>p}kj}7 o̺HE2ӴA|^&2n?%@0 *H=H0E!/)N5(llV?gOa.0  7̭U QXw #su7gq Sfp]aSfp]0Y0VɃ:;z[o?œ|0 *H=01 0 U user0 220410174126Z 240420000000Z01 0 U user0v0*H=+"bTfll <3SdV/l80RgN^ŷzbCn# e6e̫,lVj5aVӏ\b50 *H=g0ajld0.1"vpÓJ<}rߚ6ix2mb'O0eqq–da|~A{V!rj>=cqj~O _/@~O _/@iiOpenSC-0.26.1/src/tests/fuzzing/corpus/fuzz_pkcs11/96ff9ea1b05bc5d0443305fae8ace07732c85359000066400000000000000000000051461474147347300274610ustar00rootroot00000000000000111111123456;1s!WYubiKey@jjjmmmaOyO ~O a ~O _/@ ~O a ~O _/@~O _/@j~O _/@ S;09a5?S;09s9shX!B!B4)^|p`A Z520300101> SpaSp00A#4pw5?dӉl?n0  *H  010U vhanulik0 211110113839Z 221110000000Z010U vhanulik00  *H 0ƏS0Ao/e8`ʹ$=ߥ>F@NmÚz.Wa 89@U[ؒ! *6X8:na=u2m5c!z~?E$$E>jyu0  *H  fn*4kNkQ |>sv f^j$ fD`Dʳ""7+S7O{&k?jE˜q SpaSp00;B, |(M3209SS0  *H  010U vhanulik0 211110113903Z 221110000000Z010U vhanulik0"0  *H 0 p)F)uIJSd d߷&(0ϡ]@u<=}bϚg=)MᜓGl%aDIWzIh1ųiCg c@7Tayڦs}QePRH6f0w*$G]R =j *shoEؼr7( }n{ЎZhmm\EZ0i4,ν1bhT0  *H  v[a&{/>r4)e* pnf 69 h{'FXeJ[o9zaaL(-Jz0Z2a`ghBi1B3DUzFB؉EH2̥ZribȿƢZ-15"8ajWWMQ'ՙ|; x޽;t*裐G;qb5I3" 2L?fn9Q>C ׶yA"W>禁q SpaSp00~)~o0 sXtbq0  *H  010U vhanulik0 211110113939Z 221110000000Z010U vhanulik0"0  *H 0 ڑ &aWlҚh<#p]%&fй٫xz{A(~ʘ*mTl$j}'ɬ~tgҺqʴySU}J USµ#vfK"z`[S1q S2p)aS2p)0%0̠3RNHK?>|񣡨0 *H=010U vhanulik0 211110114003Z 221110000000Z010U vhanulik0Y0*H=*H=B'$ &ӸWέ8ZTGAfTq<E$r0c6K#^  _90 *H=H0E V$V<C8a686b/Ch!xSLGU7z:us.6$<]fq~O _/@c~O _/@~O _/@~O _/@OpenSC-0.26.1/src/tests/fuzzing/corpus/fuzz_pkcs11/dff1ba4f2e96e390e03144ef40b06981c1a0cf8d000066400000000000000000000034721474147347300276060ustar00rootroot00000000000000 v{73O} azmhH{NR7%˟!WƬ:zU@&,%, #l;1s!WYubiKey@jjjmmmaOyO ~O a ~O _/@ ~O a ~O _/@~O _/@j~O _/@ S;09a5?S;09s9shX!B!B4[-,`[T520300101> SpaSp00C {Ȕ KAon0  *H  01 0 U user0 220410173841Z 241001000000Z01 0 U user0"0  *H 0 6xi8S?*PueWE9i.|HE,Ln 랙;v.*`N,=er 6puas/[p&MŁpA Auتd +ܲVIRgUx@rB+e$Fo9?Uu IIÚ UǪl{3_;c&4cV~6M!γOͅR]bY0  *H  ?"G3>G |P3)iԹfb%9e7 %dQ0 -jkaS~ԓZ%S֧h+˖h:yٛk+jif 3ݭii8SCbܲF^R6 Nϧp'>}o0id( `{*+9h֮\:6*S=7 3_JR; /p7ܣ߈aղ q S*p!aS*p!00ĠZ\K‹^ q;0 *H=01 0 U user0 220410173930Z 240201000000Z01 0 U user0Y0*H=*H=BG>p}kj}7 o̺HE2ӴA|^&2n?%@0 *H=H0E!/)N5(llV?gOa.0  7̭U QXw #su7gq Sfp]aSfp]0Y0VɃ:;z[o?œ|0 *H=01 0 U user0 220410174126Z 240420000000Z01 0 U user0v0*H=+"bTfll <3SdV/l80RgN^ŷzbCn# e6e̫,lVj5aVӏ\b50 *H=g0ajld0.1"vpÓJ<}rߚ6ix2mb'O0eqq–da|~A{V!rj>=cqjOpenSC-0.26.1/src/tests/fuzzing/corpus/fuzz_pkcs11/dffd29e0ce2df7e99122dc3e5c7de81d77ffbbb9000066400000000000000000000043111474147347300302160ustar00rootroot00000000000000;1s!WYubiKey@jjjmmmaOyO ~O a ~O _/@ ~O a ~O _/@~O _/@j~O _/@ S;09a5?S;09s9shX!B!B4[-,`[T520300101> SpaSp00C {Ȕ KAon0  *H  01 0 U user0 220410173841Z 241001000000Z01 0 U user0"0  *H 0 6xi8S?*PueWE9i.|HE,Ln 랙;v.*`N,=er 6puas/[p&MŁpA Auتd +ܲVIRgUx@rB+e$Fo9?Uu IIÚ UǪl{3_;c&4cV~6M!γOͅR]bY0  *H  ?"G3>G |P3)iԹfb%9e7 %dQ0 -jkaS~ԓZ%S֧h+˖h:yٛk+jif 3ݭii8SCbܲF^R6 Nϧp'>}o0id( `{*+9h֮\:6*S=7 3_JR; /p7ܣ߈aղ q S*p!aS*p!00ĠZ\K‹^ q;0 *H=01 0 U user0 220410173930Z 240201000000Z01 0 U user0Y0*H=*H=BG>p}kj}7 o̺HE2ӴA|^&2n?%@0 *H=H0E!/)N5(llV?gOa.0  7̭U QXw #su7gq Sfp]aSfp]0Y0VɃ:;z[o?œ|0 *H=01 0 U user0 220410174126Z 240420000000Z01 0 U user0v0*H=+"bTfll <3SdV/l80RgN^ŷzbCn# e6e̫,lVj5aVӏ\b50 *H=g0ajld0.1"vpÓJ<}rߚ6ix2mb'O0eqq–da|~A{V!rj>=cqj~O _/@| .6| TnPq| ?|:wؼ| QђK| 2fH{ǐ| \Mǐ| (He| >Xli$Ɛ| >`ъG| 2Å| gOp| ~AA^5|  Dl| 4Ud]| ]7ِ|  fҳ| O@h| ?xƉ~m| W3n;9| m=| F%0 *H 010  `He0 `He 000  *H  0r1 0 UUS10U Test Certificates 201010U Test CA100.U'Test RSA 2048-bit CA for Taest PIV Cards0 101001083000Z 301001083000Z0e1 0 UUS10U Test Government10U Test Department1"0 UTest PIV Content Signer 10"0  *H 0 "fVt%BĄWQs(eMsB~;PG/׫CӺx?27?҈+Y4au{E )c\ҁ,6=zp +U(hEbdPE7'N_:p 7ģ|@V"9FEk,i[}}wn>MG pHxaFr󟳐al P+S/)­rE7[n듆,S +s00D+6020G+0;http://smime2.nist.gov/PIVaTest/CACertsIssuedToRSA2048CA.p7c0+0ldap://smime2.nist.gov/cn=Test%20RSA%202048-bit%20CA%20for%20Test%20PIV%20Cards,ou=Test%20CA,o=Test%20Certificates%202010,c=US?cACertificate;binary,crossCertificatePair;binary0(+0http://seclab7.ncsal.nist.gov0U00֠ӠІ,http://smime2.nist.gov/PIVTest/RSA2048CA.crlldap://smime2.nist.gov/cn=Test%20RSA%202048-bit%20CA%20for%20Test%20PIV%20Cards,ou=Test%20CA,o=Test%20Certificates%202010,c=US?certificateRevocationList;binary0U?GaWx륔g)s0U#0HDtW 0U 00  `He0U% 0 `He0U0  *H  T>kDq 쯞^^5N%3D9 ^4 1PLpN  ~PwC 3X]z.]/YV5$;&Gz/qLn}w`Ia[T 0vgԋ: & eH w 0٬#P֩^],=r;<m[~ƬjߣUY˾fNӐ"N9e3]s-u10~0w0r1 0 UUS10U Test Certificates 201010U Test CA100.U'Test RSA 2048-bit CA for Test PIV Caards0  `He0 *H  1 `He0 *H  1 110403194843Z0/ *H  1" Fn}ΓB)lW|nӔ0s`He1g0e1 0 UUS10U Test Government10U Test Department1"0 UTest PIV Content Signer 10  *H a qb;Z>CsKVP jBD']S͹JҚ(5܇{4 疳TY*4!~Y| ψڮH/Ts¶%}&j{$g,B[ VY=]r_5˶633uؤ.ɲ]t(SUk#ݲ\r8IvSK꼹j6˩&xih*kbH^GX9zDa +|#ܸ S(paS(p000  *H 0r1 0 UUS10U Test Certificates 201010U Test CA100.U'Test RSA 2048-bit CA for Test PIV Cards0 080301083000Z 110301083000Z0v1 0 UUS10U Test Government10U Test Department1a0U  Test Agency10UTest Cardholder XIII00  *H 0]'#d[fC:%g~ e|U59~tU_b|sMlI7v)G׮P&L9YpV~!sBJ)o±q9050U#0HDtW 0aU,tg?/Y 0U00֠ӠІ,http://smime2.nist.gov/PIVTest/RSA2048CA.crlldap://smime2.nist.gov/cn=Test%20RSA%202048-bit%20CA%20for%20Test%20PIV%20Cards,ou=Test%20CA,o=Test%20Certificates%202010,c=US?certificateRevocationList;bainary0D+6020G+0;http://smime2.nist.gov/PIVTest/CACertsIssuedToRSA2048CA.p7c0+0ldap://smime2.nist.gov/cn=Test%20RSA%202048-bit%20CA%20for%20Test%20PIV%20Cards,ou=Test%20CA,o=Test%20Certificates%202010,c=US?cACertificatea;binary,crossCertificatePair;binary0(+0http://seclab7.ncsl.nist.gov0U0%U%0+ +7U%0U 00  `He 0 `He 0bU[0Y. +7 32018949244215@upn.example.com'`HePYamphXS$B"PC0  *H D:`6a'tڙHo8fPML Sсa͚-~MqPSD`UeIjʉIz]\SEIîEYwȾCcuJ&v#Ll/840"eYzU&Hz ](Z[kDk =|ˣBa0QA|ۙ`a,.g0vMcp.c겴u)N[q S>p5aS>p50100  *H 0r1 0 UUS10U Test Certificates 201010U Test CA100.U'Test RSA 2048-bit CA for Test PIV Cards0 080301083000Z 110301083000Z0v1 0 UUS10U Test Government10U Test Department1a0U  Test Agency10UTest Cardholder XIII0"0  *H 0 VGg/ \B Ȓ 8q[TO7Բ.Ct$G3a\nS5y {^uYSMHA31dWҭTk'# I: \ً7G,'̨;m0J2pax#(PL8a-̒ \ -m7L:r"0d̃˹"Gܯ\'F&z9OW2J5lXQ/|kVQ00U#0HDtW 0UD=ieϚkjgts0U00֠ӠІ,http://smime2.nist.gov/PIVTest/RSA2048CA.crlldap://smime2.nist.gova/cn=Test%20RSA%202048-bit%20CA%20for%20Test%20PIV%20Cards,ou=Test%20CA,o=Test%20Certificates%202010,c=US?certificateRevocationList;binary0D+6020G+0;http://smime2.nist.gov/PIVTest/CACertsIssuedToRSA2048CA.p7c0+0ldap://samime2.nist.gov/cn=Test%20RSA%202048-bit%20CA%20for%20Test%20PIV%20Cards,ou=Test%20CA,o=Test%20Certificates%202010,c=US?cACertificate;binary,crossCertificatePair;binary0(+0http://seclab7.ncsl.nist.gov0U0U 00  `He0-Ua&0$"test.cardholder13@mail.example.com0  *H {e克tA$}qN=1W䲏$ =Pεkqvn X-~O 5- <ARA#LG*I#= CsJy J}ĠP3 @# i=қUzDV 4 XXΘWL$aBD +aJ1(/xu0!%TB:)1revY- m q S>p5aS>p50100  *H 0r1 0 UUS10U Test Certificates 201010U Test CA100.U'Test RSA 2048-bit CA for Test PIV Cards0 080301083000Z 110301083000Z0v1 0 UUS10U Test Government10U Test Department1a0U  Test Agency10UTest Cardholder XIII0"0  *H 0 >9pŋIUiuɗp !o 4s k>GX o&q5GU|׺/…sӼwL&ZVr?@8T)Gv4M$$NppgBkDL_%U#ctYQ2_T ^ZXkpR{Aa|;M胄PyJG,HK Ƽ&SXE `"5SfYNL 3kW$i00U#0HDtW 0UplVٴ2ڳ4!0U00֠ӠІ,http://smime2.nist.gov/PIVTest/RSA2048CA.crlldap://smime2.nist.gova/cn=Test%20RSA%202048-bit%20CA%20for%20Test%20PIV%20Cards,ou=Test%20CA,o=Test%20Certificates%202010,c=US?certificateRevocationList;binary0D+6020G+0;http://smime2.nist.gov/PIVTest/CACertsIssuedToRSA2048CA.p7c0+0ldap://samime2.nist.gov/cn=Test%20RSA%202048-bit%20CA%20for%20Test%20PIV%20Cards,ou=Test%20CA,o=Test%20Certificates%202010,c=US?cACertificate;binary,crossCertificatePair;binary0(+0http://seclab7.ncsl.nist.gov0U 0U 00  `He0-Ua&0$"test.cardholder13@mail.example.com0  *H Ԇ-Bd] ݺVC%@'sQMMsm۸n$#tE[9#yB&ҋ`߂nK%g(OB튊ڏFbw|()ڲcph1 ᤌ 0@6J:+PwjhYO->++3%#$`o4T{Λn3snƮ=~mOڮ zRN_>k&CU#00aU#0HDtW 0U,5SSz`0U00֠ӠІ,http://smime2.nist.gov/PIVTest/RSA2048CA.crlldap://smime2.nist.gov/cn=Test%20RSA%202048-bit%20CA%20for%20Test%20PIV%20Cards,ou=Test%20CA,o=Test%20Certificates%202010,ca=US?certificateRevocationList;binary0D+6020G+0;http://smime2.nist.gov/PIVTest/CACertsIssuedToRSA2048CA.p7c0+0ldap://smime2.nist.gov/cn=Test%20RSA%202048-bit%20CA%20for%20Test%20PIV%20Cards,ou=Test%20CA,o=Test%20Certifiacates%202010,c=US?cACertificate;binary,crossCertificatePair;binary0(+0http://seclab7.ncsl.nist.gov0U0U% 0 `He0U 00  `He0 `He 02U+0)'`HePYmphXS$B"PC0  *H aW;Ɋh?9T$Hbȓf/aLw-:/V[ d BV=wmp̈]AEE1s^GIꐉTiS>*pb#0:mEy^= IEcLG1v lR1hHGz ߕ]V ,qnSOq̭-דfx+X8L҂<*goZ:%>^wI u 5 gdv@WfT30}[Ȃ𱈱LsgFYn B>X_ 7gBc*#7H\t6+}̐aD=0U }$k8ـ) 7`j3cXqfzizp݌mJPzyyzEe(%j&FF" }z!귙|_q7WϻOJZY0L:Tub~n+ uoy~N]HnRr;˶F<<K{16862aca-020a-1686-ab5e-bd79d4220000}a l@QAB(Ca a qpr&aax3hb)7hb_Ĥ*oƩ ۀ9M)4P@  Sp/K-M+11p 3 = $ArX020724v2562sD49Gc6f\\9gKV1a=җG߲YZ$EƝӺj㮃C#>3 ϗpH!@uޙG*h|ł蜞`~=o71.7hb\l,?#f& 2 0DJJ7LLSUjϾb3)cc4{W7B#*Mtܨvtm8eX x 8<S9 <@, XgX'aed'f觧쁱jd $1!q 2IE@K\feegf lAJY LsJRS2Ks!@@4bdd0(3p ,83=0)AMO..+3+J.+F1-W17122[YkÓj}in6xڈi@axŽ-y߿ol+^霵i#a3- <~꼸d:ӏ7tdXAR[;ݗgH]}OΠ?-Ok0%5b>+lrX020724v2562S-T6lº-2rq ӻw+6zI2E6aM6*5-6Sk,UgF~O>#`wSnR櫜Gϥt%:nnCչwB[Uq8ygRfem.21320.nb1hb0zF? 3,/"f (SPXk VfdHIɡ iVWl|,b,"vGmf~núU`75fLh`Leb4a˳8.^`fQRR`\YYZ&U#_ N@$[8ـ鏝 7`jcjqfznc0)AMO..+3+J.+F1-+07122zFIN3O*2W#cپ;+| ќc6u[dr^et?3;v~U7,3ق/CMIWy \;Sf ~c{Lk.[o7PvRYpv5zR\{3a396a04-2962-1a39-ab62-bd79d4220000}a aQ_+ea}Z~K %s)S P0'"3-}%< tGR՟8b,1a"RH•&!J؂^}[y_[8Ѯ=XKZ:Y_=[ԯRБtTOpenSC-0.26.1/src/tests/fuzzing/corpus/fuzz_pkcs15_crypt/92f669ec651bb54b819db380603520c18b78297a000066400000000000000000000161561474147347300304210ustar00rootroot00000000000000123456J_V]bmn ]넲jB(a;~D7 :(ǏK4^3%23%i̯n9~oLZ bq$P`/NH[N'-9&p٥[];}1eփjjjjma~O _/@a~O _/@aSaSa~O _/@a~O _/@a~O _/@aSaSa~O _/@ja~O _/@aS 209s9shYfyP74Q"A_^c520301231>0 *H 010  `He0 `He,0(00  *H  0x1 0 UUS10U Test Certificates 201010U Test CA1604U-Test PIV-I RSA 2048-bit CAaaS 209s9shYfyP74Q"A_^c520301231>0 *H 010  `He0 `He,0(00  *H  0x1 0 UUS10U Test Certificates 201010U Test CA1604U-Test PIV-I RSA 2048-bit CAa for Test PIV Cards0 101001083000Z 301001083000Z0g1 0 UUS10U Test Government10U Test Department1$0"UTest PIV-I Content Signer 10"0  *H 0 M/= @lCvu0sObhcov4<5%O!AT4V TCaFlcF+‘ yKn3Zo#TU_*sՅ?ggP^ͻ1(¶:^ܿ*% h J7'ЗdA& M$YldS46{27O91TGydoMApAbyuJtOsw00P+B0>0K+0?http://smime2.nista.gov/PIVTest/CACertsIssuedToRSA2048PIVICA.p7c0+0ldap://smime2.nist.gov/cn=Test%20PIV-I%20RSA%202048-bit%20CA%20for%20Test%20PIV%20Cards,ou=Test%20CA,o=Test%20Certificates%202010,c=US?cACertificate;binary,crossCertificatePair;binary0(+0ahttp://seclab7.ncsl.nist.gov0U00⠁ߠ܆0http://smime2.nist.gov/PIVTest/RSA2048PIVICA.crlldap://smime2.nist.gov/cn=Test%20PIV-I%20RSA%202048-bit%20CA%20for%20Test%20PIV%20Cards,ou=Test%20CA,o=Test%20Certificates%202010,c=US?certificateRevoacationList;binary0U'xoT;?7#Lo L0q,\NQ"K+ܡn<8ѣTkFk+i\鞹5{橭P_R;2:;Ec4[z}fct+чā@-82pVa舔={^28^+쌞KEd܇B C%ˌkVp]g:0NI0E0U#0I.|7(,30UG<⪐.­vXhN0U00⠁ߠ܆0http://smime2.nist.gov/PIVTest/RSA2048PIVICA.crlldap://smime2a.nist.gov/cn=Test%20PIV-I%20RSA%202048-bit%20CA%20for%20Test%20PIV%20Cards,ou=Test%20CA,o=Test%20Certificates%202010,c=US?certificateRevocationList;binary0P+B0>0K+0?http://smime2.nist.gov/PIVTest/CACertsIssuedToRSA2048PIVICA.p7c0a+0ldap://smime2.nist.gov/cn=Test%20PIV-I%20RSA%202048-bit%20CA%20for%20Test%20PIV%20Cards,ou=Test%20CA,o=Test%20Certificates%202010,c=US?cACertificate;binary,crossCertificatePair;binary0(+0http://seclab7.ncsl.nist.gov0U0%aU%0+ +7U%0U 00  `He0G0lUe0c2 +7$ "pivitestcardholder@upn.example.com-urn:uuid:048051b4-2288-41fd-b895-5fe9945e1c630  *H  ,!Jm[Lu\ $oqCW;]zȰ3/a#CZcO@,:NdG6jG(yϟkq, ,\Įy C]j@0Ĺ Đ]3 ʼnld.yz菽 s jbc1J&Hs Ε֊BF_5x95"|Xm\.qjjaSp00kB0  *H  0x1 0 UUS10U Test Certificates 201010U Test CA1604U-Test PIV-I RSA 2048-bit CA for Test PIV Cards0 101001083000Z 301001083000Z01 0 UUS10U Test Government10U Test DepaaaSp00kB0  *H  0x1 0 UUS10U Test Certificates 201010U Test CA1604U-Test PIV-I RSA 2048-bit CA for Test PIV Cards0 101001083000Z 301001083000Z01 0 UUS10U Test Government10U Test Depaartment10U  Test Agency1-0+U$048051b4-2288-41fd-b895-5fe9945e1c630"0  *H 0 b~M!z}OsǿdSEHކ[ W52sҌ,k!SaaF@D.<"nPQq? \>6`#YZuv"fD[1 cI;'$oam ?n2ƒdto/rTWFiG)4v2*]42ƨeq |eﷴsԡ2cymVKG[00U#0I.|7(,30U=vYv(:U͕0U00⠁ߠ܆0http://smime2.nist.gov/PIVTest/RSA2048PIVICA.carlldap://smime2.nist.gov/cn=Test%20PIV-I%20RSA%202048-bit%20CA%20for%20Test%20PIV%20Cards,ou=Test%20CA,o=Test%20Certificates%202010,c=US?certificateRevocationList;binary0P+B0>0K+0?http://smime2.nist.gov/PIVTest/CACertsIssuedToRSaA2048PIVICA.p7c0+0ldap://smime2.nist.gov/cn=Test%20PIV-I%20RSA%202048-bit%20CA%20for%20Test%20PIV%20Cards,ou=Test%20CA,o=Test%20Certificates%202010,c=US?cACertificate;binary,crossCertificatePair;binary0(+0http://seclab7.ncsl.nist.gova0U0U% 0 `He0U 00  `He0H08U10/-urn:uuid:048051b4-2288-41fd-b895-5fe9945e1c630  *H  ⼔'I}o%+#b]HMsr\uλ fsXoy ѵ吐Ld87ZMnϜOwJ-/Вn pyPŴ˫M wɈv "-fmy%G|D۔\rF??En=ºT/<_D%s6,'MmzƷ$YX[~:1AbmϩLq,vѴlo+6-fbfd`\ĤcĤa TY>16?}SæȃY$ DTk pp1?}j{xbPR RaPfPhAA[^FII~rjQIJbI^4V! O.)/%"ԲJX2xb5hR4=nƝll쌌,k WD 2IrNy25ť_q PZ6`k <~F,,L 6l@XEY tTa\Z 9&nݫL)LR TYr3 ELl\PD A*Y 4!I0/ d$?wg= ϐHR`(t^m\q}P '(g;.%}ӥi~laţOsO딣v߷7^OFUiO'h,2yuk'߳jjÅ6o)9W_:/.nI~FW=)ɶS,xp՟sI-wL=[K0TVmuڭy;Of$9aم;MOc]>gCff^o^s1=eBsBʳNئ_+-?KCY^UY}|-ԹR^tV/oj 1OaoECCx3hb5hb]'WI]8p0ejh`8ߐۀ9M)4P@/*(/Pp/*MSp/*/J,340)VP晗_ V^ihn` ,Tt nd 'khahddbhilhbZ"q TW~qFi" 3s #M&F%cde`nbgs151229|Tx1̐UKԧ^>[ s(4hv/~Qp8ǦVZ?oE7Iq^+>Mb|"G:ʩf78.Lޝ޿u_Z/ށ)O_TgvR`͍oD0⎞T Uc[^/S~POԺq^gE]ʙ{m[|$oioZvDLdI-?\[9A;GĽ  y dA*""d۾ط7*xs A,b "G8>5½wd1)g0(3(YP-/J_?9$%$Q/(KυK'~PjY~vjS%,@77CTz)&LPy]@eϺ{?<\ߣxg ;} 1J#n^Y]g"5vNuKK9qWlUwO(yYg)+or”~f/|^V8B7ߞ:?Q)3dS`Z7;?cDc<-u"mfbfd`\ĤmĤn TY>1U+:خӶK@$"a Z[poS/۞pKzy 2E 2JJ SJRK򠱚T\~rQN~1(g8U’48A8u4dfcdeegddi\cиҠ&bԖIs ̓,..%蘠K 2@O..^>P32gaafbo0fd.΢gær4(bxU._1ynXĘ(e2LEg w015IRE@c (H>rsws  D+YE'^)gwo٩6ӚXϯajߟ*rIm(tzL\-/ul_:;fsX>Ś٪wq srgtd|Wby?𸠞ݝi.ۢci. _n0Fo'B'ImȼLU!Gb0l ;q.,Um;jsgfb۩/$s!UMnս*ߋ<?6}VwTaoQ CC++x3hbW7hb_'WI]8p6ejh`8ߐۀ9M)4P@/*(/Pp/*MSp/*/J,340)VP晗_ V^ihn` ,Tt nd 'khahddbhilddZ"q TW~qFi" 3s #M&F%cde`nbgs15122\ $Wv@VFUV;\]Uʒ6#sZEξqܱzq6;ܓlVd?v^/)7|VM!z! Zq˟ zU;,Ѻ{Rxu6uV9?s'7K;_~|]G7hDzVYo{4kλςU}q`Hܩ_+8V(_,`L\آ A9^MĎn f0&21320.nbV2hb3,m;OJ[~J A,b "G8>5½wd1)g0(3(YP-/J_?9$%$Q/(KυK'~PjY~vjS%,@h^z28PK^`96}5~EWnvw9[vZ[O wfS_&}/X6eӣyof4cwc$\}>:Ò)|Eua g-w.tٷ?!^/sUNX7k]_n:ĿC^9W_H1;ٽv S9a{G,1|S +~*HN/MC(񳹖a~GU٫6থ?O{~)9yCV'5O-"de yfvQx%G)5Ң;N<ĵڛ˓%.S`u,ek~㟎Αwl8h&{]oõ>}g|[j|*U뛯,'["*FEgQF+oao CCx3hb;ojOZv7q gƩq!'s( 0Sh2%,_TQ_^T_T_Xgh`R-3/-(L!$59#/?'?YX %@23=/3/]Q@N ȵDD2faD=EyE .fFMJ!bjbddv7n F;wӛIIl-o/~exݟ7$͝h2(.#gqIZWDo˜aͮ=m{ s^? q,3N|a>Y!ƘŞʔ\5[wMʐŴɮЌή׮mcvQ[kY8YhejRvμXh~ br.,]mLץ׿OOE|b>H]U_m4Н/> 9yA uVL~&>^ռןo5-Xs9Iܟg;63\ݠ17 x6kebd62(CZ@MUr^AJ],4IA *@ab^ #)iܝKK22K*yD-EP#ò׽n-g=4ΊO/z*jS٩gؐ7 2XU)H>/9hpf/,rnNŠ;jdsOrS}աon3sSM% 6]^*p3`68Z]o.9ƕU$V_{{Q=5Oq>ɮ5Eglm]XU1ݏ_Yo?黲_pgzjk}I'ge!gOpenSC-0.26.1/src/tests/fuzzing/corpus/fuzz_pkcs15_crypt/e52786b16a4202c5315c834788133b173bc141a6000066400000000000000000000044061474147347300302230ustar00rootroot00000000000000111111yGWXXt}ȓO A}tD~Tu XU&:kE9|^gt[tyh#^ҌFs9% ^u: &p+N5i̽/^.1B q+L:r|A~P+>ό'&ACr#t۴fS '?hI&CjrfII5/-V(+UЇtrv.ZF4Gzق8;1s!WYubiKey@jjjmmmaOyO ~O a ~O _/@ ~O a ~O _/@~O _/@j~O _/@ S;09a5?S;09s9shX!B!B4[-,`[T520300101> SpaSp00C {Ȕ KAon0  *H  01 0 U user0 220410173841Z 241001000000Z01 0 U user0"0  *H 0 6xi8S?*PueWE9i.|HE,Ln 랙;v.*`N,=er 6puas/[p&MŁpA Auتd +ܲVIRgUx@rB+e$Fo9?Uu IIÚ UǪl{3_;c&4cV~6M!γOͅR]bY0  *H  ?"G3>G |P3)iԹfb%9e7 %dQ0 -jkaS~ԓZ%S֧h+˖h:yٛk+jif 3ݭii8SCbܲF^R6 Nϧp'>}o0id( `{*+9h֮\:6*S=7 3_JR; /p7ܣ߈aղ q S*p!aS*p!00ĠZ\K‹^ q;0 *H=01 0 U user0 220410173930Z 240201000000Z01 0 U user0Y0*H=*H=BG>p}kj}7 o̺HE2ӴA|^&2n?%@0 *H=H0E!/)N5(llV?gOa.0  7̭U QXw #su7gq Sfp]aSfp]0Y0VɃ:;z[o?œ|0 *H=01 0 U user0 220410174126Z 240420000000Z01 0 U user0v0*H=+"bTfll <3SdV/l80RgN^ŷzbCn# e6e̫,lVj5aVӏ\b50 *H=g0ajld0.1"vpÓJ<}rߚ6ix2mb'O0eqq–da|~A{V!rj>=cqj~O _/@~O _/@|_F6I϶_{Bz9 =c'FhB/^&ՅQ@rz{sw IAJE](3),Sm 5.1!2=Dm! A~!i%0tb#"gKE鹇[Wo;DxTgJ(Ԟ2aJL]m ݌"Aʸ<G@gO +VUJwZq(aȡ&{.Ě[m\ 'әOpenSC-0.26.1/src/tests/fuzzing/corpus/fuzz_pkcs15_crypt/e9016daf00fb6713ea6f7fc18c55e85a4a33ea3a000066400000000000000000000051311474147347300311050ustar00rootroot00000000000000111111J_V]bmn ]넲jB(a;~D7 :(ǏK4^3%23%i̯n9~oLZ bq$P`/NH[N'-9&p٥[] ;1Xjj*o%P1<> 0?PD 0?PDA 0?PDQ 0?PDq 0?PD*o%P2240*PX Volkswagen AG VW PKI CardrdoDl/080 Digital Signature0 40 0?P0:0 Non Repudiation0 3@0 0?P0<0 Encryption0ZD00 ?Pv$oD 0F0 Card PIN0,0* 00000000000000Z0?0:0 Signature PIN00L 0?jOpenSC-0.26.1/src/tests/fuzzing/corpus/fuzz_pkcs15_decode/000077500000000000000000000000001474147347300233745ustar00rootroot00000000000000OpenSC-0.26.1/src/tests/fuzzing/corpus/fuzz_pkcs15_decode/0204f84aede3986d1add8909124e021cac32bec8000066400000000000000000000027001474147347300310230ustar00rootroot00000000000000/0-0 jjelen00   ;1E%VWSC6508mz0&@@GSNPBP)xcf`cJIK6d L@A83=/3/]!;R!-H ()nlz3#/VGw^FFnV/CuU6P.dԔ\Ң̒J\[}Dуe?elճV,js["7&~v2[M]w |o45\Y%b9_fm/YR\2gs1֮tä)1=)7,ez1991;.|k7ġ^$"o;yncFVipOZaĦ a!C%̸AY)&r$0Ud蕩, 3KLuťsllV?ݒInco[ꍦϋ|twb]ɡֿBɏzch1rIӖ_k';2VfgoƝSQz,ְzyTXPyg<w}Vص]wp1LŪ@t{÷-\qSpY(T2EGY'i7Žӻs*"= -Kk8{RfҲЫ3axp6HyxF&qLRr*ּe3*V{8OZ`b],AفUKbzow_s@{k=„Eo |tx: ^-* 3w%T%O8egmϐڿrS={'OݱbMN32Ll?}++zMA&`0S 7!e*fOpenSC-0.26.1/src/tests/fuzzing/corpus/fuzz_pkcs15_decode/0e9c8b959346f4894ea97d7e3f393c2442ee1e3d000066400000000000000000000062441474147347300307460ustar00rootroot00000000000000Q0O0 Card Capability Container0( Card Capability Container `He[0;1Yubikey4jjjmmmaOyO ~O a _/@~O _/@ ~O a _/@~O _/@~O _/@j~O _/@ S;09a57s9shX!B!8B4SM%B)&8520300101>?S;09s9shX!B!8B4SM%B)&8520300101> SVpMa0I010  *H  0C10U Example10U Example Test10U Example Test CA0 190703094638Z 220329094638Z0310 U jjelen10 U jjelen10 U jjelen0Y0*H=*H=ḆZ\ qOӐSY^̟` 3W)m˿ naSVpM0I010  *H  0C10U Example10U Example Test10U Example Test CA0 190703094638Z 220329094638Z0310 U jjelen10 U jjelen10 U jjelen0Y0*H=*H=ḆZ\ qOӐSY^̟` 3W)ma˿ n){ja$rtF'ã#0!0U#0rv pZ{D<ӡ=0  *H  AۓʅHFE\_<ˈ4B7ٜ7HE-r==>cmUoDՓzQ|ylM|d^(M 9 fTF %GCt;+Ip݋P0Du?KI? fo҆$.B9D|a]_2˫ؽJcDSf}%AS+},AӉv< HuЪOdɹ4|~O4yD'L#+va "K[;8דqb,`G.CMQ#4H*O6DL=C'7yaSp00 ]ό_0  *H  010U SSH key0 171114115934Z 181114115934Z010U SSH key0"0  *H 0 ט>< HuЪOdɹ4|~O4yD'L#+va "K[;8דqb,`G.CMQ#4H*O6DL=Ca'7yhɿ6E mcȸ U.HuS0i GewjģVQ28Vk"#GC Pܰ@No(y*+b3VNKCʧo8"xkKUU+ZjThC,Ȕ ̂dA0  *H  q Slpca0_0 ;ˊ`0 *H=010U SSH test key0 190311081112Z 200310081112Z010U SSH test key0v0*H=+"b|)<éygp,d7/{ΚRUV&%^-ʠtim- x^TYYLQEv RN:R0 *H=h0eaiSlpc0_0 ;ˊ`0 *H=010U SSH test key0 190311081112Z 200310081112Z010U SSH test key0v0*H=+"b|)<éygp,d7/{ΚRUV&%^-ʠtim- x^TYYLQEv RN:R0 *H=arth0e0?jnFƃ˝YIlˤ\/(ηķMe,`1؈ Ju>F7Mub%^tm}i{èC>,1uq Sp0a00  *H 00 000001010000Z 000001010000Z000  *H 0{lh5,PS9/|EtNAR7 ;daR #!JCr#`[HI⊮<B8^AuR*> JxWHQ_TtTh\h0  *H qSp000  *H 00 000001010000Z 000001010000Z000  *H 0{lh5,PS9/|EtNAR7 ;daR #!JCr#`[HI⊮<B8^AuR*> JxWHQ_TtTh\h0  *H q~O _/@cOpenSC-0.26.1/src/tests/fuzzing/corpus/fuzz_pkcs15_decode/6e580d278c33a530284dfef5dd9ffd617597bb68000066400000000000000000000074251474147347300310230ustar00rootroot0000000000000000 CAC ID Certificate0@@00  *H 01vj<PDy)qz9M% ٣4k6P 홋 G FNqW]vBwC/ǀWXo:rc[e_7u9;25nį]W9;}1emmmmmmmma o a o a4a46a ay@pPr6X,!!yyyyyyyy00`0`000yyaya l@QABRCa a qp]r&aax3hb7hb1A߀Sͣ;/##++A!'s( 0Sh%_ZWbcfvwAx{x|.. ^! Ύr&F`%khZZFSv/4P@ ? =_ OW=CƖab_e$;wd۰"Q[Ś/ ,}UN=vasɚf~|;{%7n 9;b>:,w?ް[xi|=]ƛ*62] 7Umő/wnxҾe-qנ@Ye`4  U&QrB&'z @ YltqaFII~rQ^^f^Jfq^nf~zj P?FR88 dX x 8<S9 dA|,ba,"\ag}7^٥pt:6m6`bgdd)2(0 ڌ83=۔ ˊQ29E@@Ő=s##\'9YIo]WjA)Fߋ94? 'zW?W0!.)SzϛN\*rk)VCwMctҹ-)B*B,yL-@a^/%8<*oq<}͕[h'J6.kCvVEx᭨ޱ=-Vys}qq=iq'*2W5U8@tVWVYb;7|N{b2758b1e-508a-1275-92da-98506be00000}a l@QABTCa a qpr&aax3hbaܰƀSͣ;/##++A!'s( 0Sh%_ZWbcfvwAx{Hx".. ^! > Ύr&F`%khZZFS ^hE!A~AzAz@[͍-- ##+sc/Ac'Sc#eHJ)-TxAnTo aXEg%.yhRU֬wr= hvH<)ӆ3y|ŋ֭3)gq6p4_`2J_?(G//D/%8Q/73G?=(f%U#P<)pLiH\gdv ,l maLܜ q>1*Go0}n9PѠC Y * `|6+<+L>=9mJPs ˊQ4SQ((/ȡ(5%#D/9?w8&s&<|Jײ,"B09Y Y@ @$ʢdd L3ł똛V$/O|v0ؾ~lgDqvMohHaRxN]+{,]f|NyIgW^|&v8NFޕqBg"Nd?lʦQ{_ۼkWm ?>to\̹~vI9\?Sf1h{쳋m T.ߓ2U69O2w_+C'gEnjNު[Ulsl{c75bbada-529a8-175b-92dc-98506be00000}a l@QABWCa a qpr&aax3hb5hb6][kƩ ɐۀ9M)4P@  Sp/K-M+11p 3 = e A=h$!l†>_ Y+G׸Ϫ/;.vgv6h&fFMM@*ATIO>$xu @ Y  .n())O.,K,NOO-cIO@ S@W| 03*BV߀׀3̓a1@$""F㝹&ӄQwlOg4hlcf0vFF* 7߀ 3OncԜ<b#!d3i-)-K-r(JMH,K[YN!ɜ,,UgNtmZɥX iL2'xx?讏x'6|0t?9mGS|ѬRb,7QU͋aNLiIx69NOm;̻{"{ 7^2)G.|+vt\U.qt}RS.C&69>;?JsǞ8=F}z-;r:sLtm4~1sr;+}75>!)gRZ J:]tNc{d9857516-0054-1985-92de-98506be00000}cOpenSC-0.26.1/src/tests/fuzzing/corpus/fuzz_pkcs15_decode/8989be8baa0b0269c8128729062b31f91b131ba4000066400000000000000000000071471474147347300305430ustar00rootroot00000000000000,0*0 PIN00 ;}1emmmmmmmma o a o a4a46a ay@pPr6X,!!yyyyyyyy00`0`000yyaya l@QABRCa a qp]r&aax3hb7hb1A߀Sͣ;/##++A!'s( 0Sh%_ZWbcfvwAx{x|.. ^! Ύr&F`%khZZFSv/4P@ ? =_ OW=CƖab_e$;wd۰"Q[Ś/ ,}UN=vasɚf~|;{%7n 9;b>:,w?ް[xi|=]ƛ*62] 7Umő/wnxҾe-qנ@Ye`4  U&QrB&'z @ YltqaFII~rQ^^f^Jfq^nf~zj P?FR88 dX x 8<S9 dA|,ba,"\ag}7^٥pt:6m6`bgdd)2(0 ڌ83=۔ ˊQ29E@@Ő=s##\'9YIo]WjA)Fߋ94? 'zW?W0!.)SzϛN\*rk)VCwMctҹ-)B*B,yL-@a^/%8<*oq<}͕[h'J6.kCvVEx᭨ޱ=-Vys}qq=iq'*2W5U8@tVWVYb;7|N{b2758b1e-508a-1275-92da-98506be00000}a l@QABTCa a qpr&aax3hbaܰƀSͣ;/##++A!'s( 0Sh%_ZWbcfvwAx{Hx".. ^! > Ύr&F`%khZZFS ^hE!A~AzAz@[͍-- ##+sc/Ac'Sc#eHJ)-TxAnTo aXEg%.yhRU֬wr= hvH<)ӆ3y|ŋ֭3)gq6p4_`2J_?(G//D/%8Q/73G?=(f%U#P<)pLiH\gdv ,l maLܜ q>1*Go0}n9PѠC Y * `|6+<+L>=9mJPs ˊQ4SQ((/ȡ(5%#D/9?w8&s&<|Jײ,"B09Y Y@ @$ʢdd L3ł똛V$/O|v0ؾ~lgDqvMohHaRxN]+{,]f|NyIgW^|&v8NFޕqBg"Nd?lʦQ{_ۼkWm ?>to\̹~vI9\?Sf1h{쳋m T.ߓ2U69O2w_+C'gEnjNު[Ulsl{c75bbada-529a8-175b-92dc-98506be00000}a l@QABWCa a qpr&aax3hb5hb6][kƩ ɐۀ9M)4P@  Sp/K-M+11p 3 = e A=h$!l†>_ Y+G׸Ϫ/;.vgv6h&fFMM@*ATIO>$xu @ Y  .n())O.,K,NOO-cIO@ S@W| 03*BV߀׀3̓a1@$""F㝹&ӄQwlOg4hlcf0vFF* 7߀ 3OncԜ<b#!d3i-)-K-r(JMH,K[YN!ɜ,,UgNtmZɥX iL2'xx?讏x'6|0t?9mGS|ѬRb,7QU͋aNLiIx69NOm;̻{"{ 7^2)G.|+vt\U.qt}RS.C&69>;?JsǞ8=F}z-;r:sLtm4~1sr;+}75>!)gRZ J:]tNc{d9857516-0054-1985-92de-98506be00000}cOpenSC-0.26.1/src/tests/fuzzing/corpus/fuzz_pkcs15_decode/b011d577451c835fd8f6052f0659337994273f3f000066400000000000000000000052361474147347300303510ustar00rootroot00000000000000A0?0 CAC Cert 1600@00 ?;1ESCE7  FBjjjjmmjjjjo ymo yd Ay Py `y By Cy DyE CID_o y@ QAB C QAB C QAB C QAB C QAB C QAB C QAB C QAB C QA B C o y@ QA B C QA B C QA B C QA B C QAB C QAB C QAB C RAB DRAB Do y@ QA B C QA B C QA B C QA B C QAB C QAB C QAB C RAB DRAB Dr(qp,gd{EC65ECE3-9D4F-46DC-8B78-07541319A0E1}x3hb`|c3#S/VGw^FFnV$CnN6P6a`C 1K?T/XO=,(/75Đǀ $- 45E\] ;*l6ga)=ٟt/czͣ^;!UQy?~,6>kOzqIM>ˣ,7-]&4ڛ͎PpjY߈O·56=QOWThu>O(he7EL~dCAK/͊wŎERxdgx:^+eӐsvFn}rN"}CSf6Hm-NժM_R71Ty`P*AyO7)u/ޛw|KIY>1U>_nUf{cZ6m6FVVvFFB|[߀0J_?(G//D/%8Q/73G83=O@%81\/9@M =?@/8@($>03VE@fPb1I 886gMjNbf^^:0!%9&f怌^ d.ļ@@Vҳ 7gBc*##H\ ,0j3 @,PA6GC7n'ϖL_jJs.XP (O>{=x]V_G},M +y]]  64iG[}b+Ɛ2 ˵ՊzjJKcƢSSkBz͜:aN;I'n3c߾krϫ}U:כ5'ola_UUΥgX5,[_~m /ۖyE+Y{T[Yvxkb```b&&F Il̡,lLb 0^{~YjQ^nj^!H[%i(k 1 (xy8+x(+8;3220T*]_Hs܋R+R, ,169CSJynZ#3P!e:Dxcbd`P````?x=S'ܴxGf3rC26;9+t T#Ez^yzf .@c*l̡,lLb 0^{~YjQ^nj^!H[%i(l q99:HE ^=۴^-:vւɊb|,w|UشmªSz:.?-^DG0vC~Xl| | ;*l6ga)=ٟt/czͣ^;!UQy?~,6>kOzqIM>ˣ,7-]&4ڛ͎PpjY߈O·56=QOWThu>O(he7EL~dCAK/͊wŎERxdgx:^+eӐsvFn}rN"}CSf6Hm-NժM_R71Ty`P*AyO7)u/ޛw|KIY>1U>_nUf{cZ6m6FVVvFFB|[߀0J_?(G//D/%8Q/73G83=O@%81\/9@M =?@/8@($>03VE@fPb1I 886gMjNbf^^:0!%9&f怌^ d.ļ@@Vҳ 7gBc*##H\ ,0j3 @,PA6GC7n'ϖL_jJs.XP (O>{=x]V_G},M +y]]  64iG[}b+Ɛ2 ˵ՊzjJKcƢSSkBz͜:aN;I'n3c߾krϫ}U:כ5'ola_UUΥgX5,[_~m /ۖyE+Y{T[Yvxkb```b&&F Il̡,lLb 0^{~YjQ^nj^!H[%i(k 1 (xy8+x(+8;3220T*]_Hs܋R+R, ,169CSJynZ#3P!e:Dxcbd`P````?x=S'ܴxGf3rC26;9+t T#Ez^yzf .@c*l̡,lLb 0^{~YjQ^nj^!H[%i(l q99:HE ^=۴^-:vւɊb|,w|UشmªSz:.?-^DG0vC~Xl| | 0?PD 0?PDA 0?PDQ 0?PDq 0?PD*o%P2240*QP Volkswagen AG VW PKI Cardrd$oD 0F0 Card PIN0,0* 00000000000000Z0?0:0 Signature PIN00L 0?oDl/080 Digital Signature0 40 0?P0:0 Non Repudiation0 3@0 0?P0<0 Encryption0aD00 ?P0<0 Encryption0bD00 ?P0<0 Encryption0cD00 ?P0<0 Encryption0dD00 ?P0<0 Encryption0et00 ?PvoDA/0/0 Digital Signature@0 0 0?PEA0-0 Non Repudiation@03 0 0?PEC0/0 Encryption@0a00?PEt000 Encryption@0b00?PEt000 Encryption@0c00?PEt Ȁ000 Encryption@0d00?PEt000 Encryption@0e00?PEtoDQ/060@0225118030566055813900?PEtl080 @0468135259511316737500?PEt"q070 @0818677575340868483800?PEsi090 @0-103714765826740230900?PEsiioEA /00J0  *H  0\10 &,dcom10 &,dvwg10 &,dVWPKI10UVW-QS-CA-OTHR-010 161021074212Z 201021074212Z0G10 &,dvwg100.U'Musterfrau Maike VWPKI FB247F21CE25B7BE0"0  *H 0 qhڙ+h!#fN;_VGa]S, Ր0NJ} gyӡࢿ򮱓8e<3S="SԈpZ=hr 5 9F UCЙ˹O4k4Kkѓ<2=8^rh)Т-y[v.fhtoJ쬋 3~L:#@n[ ݻA2Y5 w # {00PUI0G%extern.maike.musterfrau@volkswagen.de +7 VWMUSTE@vw.vwg03U%,0*+++ +70U# 0 K%Q+:'r0H+<0:08+0,http://qocsp.volkswagen.de/VW-QS-CA-OTHR-01/0U 00+ [0U0031/-http://crl.volkswagen.de/VW-QS-CA-OTHR-01.CRL0JHFDldap://ldap.volkswagen.de/cn=VW-QS-CA-OTHR-01,dc=vwPKI,dc=vwg,dc=com0U Bf-*0U0*p" 11511250 U00  *H  t~ +Fy4Y{85`y>E{C)-a?]eTJY͏mcxFA82k쌧seKY] sG,pm8 [Y !껆K[zOP_!R`660mLj)zH֋/2KC` 钔T_ѹz?tՐ3 $qk&ṁNG-/uNX߿MxэgH!{5+XֵX̽iz(*8_"!im-[S2膇O$s9s qwX )T(UEz:@YxM66м鑍:`ZV611$റL8Q՞+n<3&1??!yC0Ry=v]rum] *C^O^EsZ0H+<0:08+0,http://qocsp.volkswagen.de/VW-QS-CA-ENCS-01/0U 00+ [ 0U0031/-http://crl.volkswagen.de/VW-QS-CA-ENCS-01.CRL0JHFDldap://ldap.volkswagen.de/cn=VW-QS-CA-ENCS-01,dc=vwPKI,dc=vwg,dc=com0U L܂*0U000U)0'%extern.maike.musterfrau@volkswagen.de0*p" 11511250 U00  *H  UTk+uq#EJhAp8 ZL d=2Uƍք9(A펺LOIG_YE̻:\Zt+*d:Ҷm:")Sna]#'Ə"(l9dElGAb?oŠg#@1אlW]݈]MUF\`'H{/ŒۢE |By_\Jz{EAsDWnoD ^@B#\ n8F{b=݁q(fҌQ!͵{F}walftmեGq/n`0A:4"T 4`c4KtjCH4{Ȅ~EgA؄$p܀T5-H~dy^-Eb&WLC33&e+X6(@7<_\3vŘMK8Ip;ns@{*4"zKMiz: zQT*o%Et*00ȠI0  *H  0\10 &,dcom10 &,dvwg10 &,dVWPKI10UVW-QS-CA-OTHR-010 160609131733Z 200609131733Z0G10 &,dvwg100.U'Musterfrau Maike VWPKI FB247F21CE25B7BE0"0  *H 0 O~\MƴJK}!RJӇj 9my]y_Щ-T/;ۈ74$Gjcf-I@dOq̡3,a^UGMexK)Г2й䪆]"gwzcP4a9px5UۥC>:rL̴ 柇gߥ2JnH\ \IJ{L zwCD=ȑ8\{C FY[W^{R000U)0'%extern.maike.musterfrau@volkswagen.de06U%/0-++ +7  +7 0U# 0 K%Q+:'r0H+<0:08+0,http://qocsp.volkswagen.de/VW-QS-CA-OTHR-01/0U 00+ [0U0031/-http://crl.volkswagen.de/VW-QS-CA-OTHR-01.CRL0JHFDldap://ldap.volkswagen.de/cn=VW-QS-CA-OTHR-01,dc=vwPKI,dc=vwg,dc=com0U ID_0U00*p" 11515880 U00  *H  ]GlTC_|µXL0=]aGcL;3P?J~](f@[)ޙCc ϡɬ:W]a0F'KJE;ݑWBGHqnd#8݌0r`=)"dxl9M}4*֫I5zF"]٥h$z[!\` p՜MPcq ,*p2ɕqUBOQ[1-@红{Dɛ ^fFk97lKACwx}τ2ٺ`u5!``dGGK:wTT9ފ iI;X{\4yڒYJ5S__(8}\eeBΧO qpz5,p*o%Et*00ˠI-0  *H  0\10 &,dcom10 &,dvwg10 &,dVWPKI10UVW-QS-CA-OTHR-010 160519142351Z 200519142351Z0G10 &,dvwg100.U'Musterfrau Maike VWPKI FB247F21CE25B7BE0"0  *H 0 M ћa&Gi)b6~)` X#ѯ'.Bg}țF{4doҬJqӏw>n*V{VצgNJczpVHSk>jAuw~vx[۳r) o=̞1;)j Xg"ٳU, Zn3j Mq0Ye7?0#'{=&y4#000U)0'%extern.maike.musterfrau@volkswagen.de06U%/0-++ +7  +7 0U# 0 K%Q+:'r0H+<0:08+0,http://qocsp.volkswagen.de/VW-QS-CA-OTHR-01/0U 00+ [0U0031/-http://crl.volkswagen.de/VW-QS-CA-OTHR-01.CRL0JHFDldap://ldap.volkswagen.de/cn=VW-QS-CA-OTHR-01,dc=vwPKI,dc=vwg,dc=com0U MP5 80U00*p"  10550003820 U00  *H  an{Hx/.K07mwW) x3bQ`TV2mxEuz\f W@v)VA\ϵ#]000U)0'%extern.maike.musterfrau@volkswagen.de06U%/0-++ +7  +7 0U# 0 K%Q+:'r0H+<0:08+0,http://qocsp.volkswagen.de/VW-QS-CA-OTHR-01/0U 00+ [0U0031/-http://crl.volkswagen.de/VW-QS-CA-OTHR-01.CRL0JHFDldap://ldap.volkswagen.de/cn=VW-QS-CA-OTHR-01,dc=vwPKI,dc=vwg,dc=com0U LbU10U00*p" 11504650 U00  *H  5cy7@_ v:E$l15-L#E}k?.KoIK&҅* =hJC|j`LW%c *6ydP4EkHgʥŒ1k| }qB[''r:BlOw5+MO8dq `JOeĿ& лu9xahŤZT {K9FBlTM-+2Ґ4-ʹhL}+?4U.gJEЇ (d]N_ 6`GEKtS r+5vfJ>УD' fSkWeٮN-QaPUv(zDL9Ul^iZ0ZX٫KB5޷tg\ӴfkrFYcMF_D/.>T TAy.̕'n`n 17/{ ']:0X:Zġ!bi641dTo<N4BK_' C00@U%907+++ +7  +7 0U# 0 KFql0H+<0:08+0,http://qocsp.volkswagen.de/VW-QS-CA-PROC-01/0U 00+ [0U0031/-http://crl.volkswagen.de/VW-QS-CA-PROC-01.CRL0JHFDldap://ldap.volkswagen.de/cn=VW-QS-CA-PROC-01,dc=vwPKI,dc=vwg,dc=com0U Cn0U00U)0'%extern.maike.musterfrau@volkswagen.de0 U00  *H  4}u1Xl;- +Nʿ|kdHO"9a3G:fܘ-"k(qH;12`iZ5|28:7DUl i7O-;f_9u3GKqbE+dAv=a* zY㤧pTѥ*ߕ7v_ak?3]$h؇etjČaيQ<5Xa7igCXHCL5z^ro|);~܍@!dH FEv%aԗ:8t!JR$tZ$U`*]'Ͽzfje(!~7Lp ,JݝQKW#1\7ERG|?5J^ ËQ :o!dF1 AY~Z. Cm?QE8S#?<[}咒+wO-RPcbP`>cJu|ي-EݜRuOy<5\?,_VJ8$:3]h\!G.?O$$k#kΗ}r̦!u0t#gH"P QL_2eJES0Q0U00U GY m}0U 00+ [0 U0  *H  E{Ri6hr%(/!w{M$s“r hܧPzg^R~&Pg8REk>W* fuF񳀓5moK I $wDZ*rWɼyp4jiVG'+ w9 - !_E]aPH⥢&AHQud$Q<¹Cu-uB*BvcK;ߴU(0GWT_nŐ],c[疢l҂Emf Vΰ+5;G_=^x:n˗%OH dm' wtHǛ494ʈ±ثeUq!wIfPEa4'0,L@ ~\f)+#p,g{*o%Et*0m0UB'0  *H  0\10 &,dcom10 &,dvwg10 &,dVWPKI10UVW-QS-CA-ROOT-010 130606100000Z 210806100000Z0\10 &,dcom10 &,dvwg10 &,dVWPKI10UVW-QS-CA-OTHR-010 0  *H  0Y9D8;m ĪU_}BݼWFyA Hf@NS7o^>u9iӫ 8O ),UֳA\ w+1z6^1x ]Cpip~>=PFD5f>la{h_*)z N =yX6J>%2OA ;Lz+döQ֛@ĊiL gm::Rۤ#߹fC7su{ܶ@v9B%JƤV4感ZSƶF*1nr% ޓ!QdfBw՘e#(~yhGX6ܥtɃ` 5ێOb5qo NsQ%EŎW~&E/B@tQL $@yh=WjL`hQ? [ BnSH2Ƀ oϝqTA9050U00U K%Q+:'r0&U 00U 0+ [0U# 0 GY m}06+*0(0&+0http://qocsp.volkswagen.de0 U0U0031/-http://crl.volkswagen.de/VW-QS-CA-ROOT-01.CRL0JHFDldap://ldap.volkswagen.de/cn=VW-QS-CA-ROOT-01,dc=vwPKI,dc=vwg,dc=com0  *H  Oa.V?brbX gx5-7b_mToЫjG@{3iҗ7lStOflH, 4QP7M~%+)*Ɣ8V)^*%&'rOUOz0L ˢS7>L+rRTIML9 vMS&S_!@zbrG^*5P ; o;8:Q(4O'o8q{g6BsӫuۭUyfFr ɭ  3?~o,srۮOw;f$Y}ǜd j9+~Na]BU(E{N?a_U-;c~tP]f3FTt,P dمVS5]@ѧ@-h9}vRh|,kk՟BC%C$O*{od J#&*o%Es*0e0MB%0  *H  0\10 &,dcom10 &,dvwg10 &,dVWPKI10UVW-QS-CA-ROOT-010 130606100000Z 210806100000Z0\10 &,dcom10 &,dvwg10 &,dVWPKI10UVW-QS-CA-ENCS-010 0  *H  0ږfVgX^a 䈤9rJO/qRFWTjπp B R7sqS/ϕw:]"_E~=(ݐR `g"wA}v2U{ -Ǒ㖈'ϙYmP\vd⼟Nϣ,$v9;b#鉌'bt?<΅ 'S\9yg^TKe/LASM'h,@-)}oTη"8maȔo@C=_ͣrC<kN^gTcv̎1D1ӠEnacKSFmF!n#RHn|]N#| P꾷7/)Sغ=165RewFAeb5* 3:cwټ:`VquAO:h!_?SB852Rlz{h;$&0z}9|0.mS#% lrcO&\ &J5SB9W10-0U00U L%>sZ0U 00+ [0U# 0 GY m}06+*0(0&+0http://qocsp.volkswagen.de0 U0U0031/-http://crl.volkswagen.de/VW-QS-CA-ROOT-01.CRL0JHFDldap://ldap.volkswagen.de/cn=VW-QS-CA-ROOT-01,dc=vwPKI,dc=vwg,dc=com0  *H  v/(bj$oP JcC{gzӑ\U?17; 3:P0L_4 8dE\yMFȓG Uv2 \LyKx(rZOY_)YE4>VMQcjR%d}-G|$OПb>([L@NɍnФ=IB/#5zTqeOVtÁLF<1UK2X ID-93QHjXx,VYM5WN%նO,Z]u50@{W,|Dֲ΄_WeېMd@ZѬ; H򊇐s|*r[3 k(%IdYҐHRvU;afn'k-⦏e 1z!qًH6 ]vR}C)&zCsl"+>*o%Es*0e0MI0  *H  0\10 &,dcom10 &,dvwg10 &,dVWPKI10UVW-QS-CA-ROOT-010 160606100000Z 210806100000Z0\10 &,dcom10 &,dvwg10 &,dVWPKI10UVW-QS-CA-SIGN-020 0  *H  0F+'D!Gh</'_J3ҵf/krC |4G:ur$CT1ANhݒP~7Xti\}G"„*c51νB7.X$s%E ;ScBYa Y!ɩ<|f`_ڗ{* #v/2*H'e=y$û釐Wdwd7C܊cw)%t>$& b4(Uf ōvJ4@I6|k{c $td|*VWb[wld$3iI;U}MY%-[1WGxNR͸+%T ~0a}/b{FrMf2. [}.'10-0U00U DTlVD0U 00+ [0U# 0 GY m}06+*0(0&+0http://qocsp.volkswagen.de0 U0U0031/-http://crl.volkswagen.de/VW-QS-CA-ROOT-01.CRL0JHFDldap://ldap.volkswagen.de/cn=VW-QS-CA-ROOT-01,dc=vwPKI,dc=vwg,dc=com0  *H  +L~ˠ- IU񫷞-J0c(e ؏@aVÜArDg ,lH(`# ]jUx@и$}rpz?LA!J@\9LKO:$ X+,5Ucwa=OO}ONLKe=WKseI:_ Ip=}uQtפX\*065XFb~諸{jc@J:;c8H[F)I'Q*Ə/TS(XzNG4bi0 *H 010  `He0 `He 000  *H  0r1 0 UUS10U Test Certificates 201010U Test CA100.U'Test RSA 2048-bit CA for Test PIV aS 0PX홒j١'DPC4520301231>0 *H 010  `He0 `He 000  *H  0r1 0 UUS10U Test Certificates 201010U Test CA100.U'Test RSA 2048-bit CA for Taest PIV Cards0 101001083000Z 301001083000Z0e1 0 UUS10U Test Government10U Test Department1"0 UTest PIV Content Signer 50"0  *H 0 ޷vIsS~1SasL%04dZ,$smt9Y,4p\Ta[Z;5JZ:( J % };5•_l$ N"_M(=AQEnmB;e 39%œ> U#؜4|iɰ=e`+S7ډ A44kܒ ufʢB֕$]اD y.T4һ00D+6020G+0;http://smime2.nist.gov/PIVaTest/CACertsIssuedToRSA2048CA.p7c0+0ldap://smime2.nist.gov/cn=Test%20RSA%202048-bit%20CA%20for%20Test%20PIV%20Cards,ou=Test%20CA,o=Test%20Certificates%202010,c=US?cACertificate;binary,crossCertificatePair;binary0(+0http://seclab7.ncsal.nist.gov0U00֠ӠІ,http://smime2.nist.gov/PIVTest/RSA2048CA.crlldap://smime2.nist.gov/cn=Test%20RSA%202048-bit%20CA%20for%20Test%20PIV%20Cards,ou=Test%20CA,o=Test%20Certificates%202010,c=US?certificateRevocationList;binary0U_aƾ+8l0U#0HDtW 0U 00  `He0U% 0 `He0U0  *H  Z Hd&QG2wK) ⡎ x ?Sr)cYdj XWIcΆC~"rC: hZÇR*oQ苵1#g xڸ,o9"sՍ@5eajfva$I팵cAYWkU׋;dʎW;NY5RGRhجMXN"r:;y0Kz1,`|̻.#cͮSI gT|10~0w0r1 0 UUS10U Test Certificates 201010U Test CA100.U'Test RSA 2048-bit CA for Test PIV Caards0  `He0 *H  1 `He0 *H  1 110403195841Z0/ *H  1" A.m6uv{3Fu0s`He1g0e1 0 UUS10U Test Government10U Test Department1"0 UTest PIV Content Signer 50  *H a Bj /UDr5 ~ZqbM ,D䲢YO` ۻ-K:ǫ45')XeN'R’ f$mhȈ;S%ŝE{K>WLSXEhACR=5 U/WaeXt Mb AGņXNK-9ga G"r" Spa3hbgڶҀSͣ;/##7+A!'s( 0Sh,%,Z\ZTXZ`d`h`(`R-Qhh`aV;UX&ea ΉE)r@ JkP& 1痥d2.E%`! 7X1=5/P@ TaのII-R 3hbTBFV&F~8S##1JaSp3hbgڶҀSͣ;/##7+A!'s( 0Sh,%,Z\ZTXZ`d`h`(`R-Qhh`aV;UX&ea ΉE)r@ JkP& 1痥d2.E%`! 7X1=5/P@ TaのII-R 3hbTBFV&F~8Sa##1JUj;/,ܐܧkk5;{]^S=K̗/lnfv}ԒԓdUuԣR3\%};(Ӓ\[},׶ۇ'>qiR|2rˏwڶӭ]_J ֮~4b}r44^Hsڃ#˝ߋmi~759ߗ'vȘ#8#ˁnu*Na A҅f21320.nb4hb652 0 Dylkӄ%gÏdXXDV]ԷYgf7RH9>Ӡ H^AMk //hЦQRR`_jY\_L=8&/PrvK.ikMyr-HPH J U4$J:0Ύ:p6R>fhllJ-ҙy>@WX'e%U 6#++;#a#KP&ffMΎ ۋ=KSSBP`lиɤHRI./.FH,m2pHMIL2K.΁{\32gaafbo0PUY d`F03qY,M"LLl,@iq2`eƕ , y 8!<6NFf`6H)d6\d9c , -M ,LJ R+s rRsq@ c[ "y-@"b朷3']usY-.m_ _faPҲY-e-ul_8~jp/:\Q 6ONw* YOOR^n§%gf3QDr'/o\H>u^@jݟ{6ye߸leKWO`ꏻl~Xt0{S+g?qJIV)Wf=|w/O]1ǥܙ2=''f>RIH_6gW3gz6 .3 y'1fVgd[)εJؠ H^AMk //hЦQRR`_jY\_L=8&/PrvK.ikMyr-HPH J U4$J:0Ύ:p6R>fhllJ-ҙy>@WX'e%U 6#++;#a#KP&ffMΎ ۋ=KSSBP`lиɤHRI./.FH,m2pHMIL2K.΁{\32gaafb;` +#66T`n dQ3PiTD/ Mr3sR+s rRsъCfP>wgȘė ߾A'/Eؤ(i;Tk(~U_6_۽%i#y+Vxhߛ+cUW)Gm5ax&[Z-D ߇O~aۓ#u>;iN1.Y0-XEK]+p.~x![ԃ/ \Ohx^O-L>uDc}oX+gҾ$P}Љw,ۛO q+y϶8̚@bWWX6߈^8gc5ҹ 4q Spa3hb30hbXZcƩȐۀ9M)4P@ I-.QpN-*LLN,I-V020400)f(p4400 E*M,t2A iE `0Ģb9q^I@dal``%k5(EuM`sRrSJ`2PԂĢD,㘞\i(c q0@d礤)Dx41*!#+s#?PᶾaSp3hb30hbXZcƩȐۀ9M)4P@ I-.QpN-*LLN,I-V020400)f(p4400 E*M,t2A iE `0Ģb9q^I@dal``%k5(EuM`sRrSJ`2PԂĢD,㘞\i(c q0@d礤)Dx41*!#+s#?Paᶾ7[מ|hľ-r?Տ[+uWe0CkWEP 0qҳ[_Xx{f~l~Cc١L`ذ} h[_CHS+"5+1SLX}'sו Y44h m:%%VŹFzy%ze3}`.gG䢜9)ؔ'ق5IX2A|`P%@A⠴_ 쨓g#i:ɶԲ| tuRf^bQ%0qh132a41E abLm|س45%${Lj\?С$ b$"Xh]& $s,?#f&Vq_Xq%y08fv] J}%z\nh␛Z[V29;Ą{/_U佥-8P?e@rl\߂o~tXKزԝgj>-Se^dƵNbO!bAEag3$}zwuզDŀ;/'y6חB:)MNsOR>vW'<5Inۂ/rK'y;7ݛyϜGel1x85&~_,҇o^??]|k>--"K[k7m^5;j u4q Spa3hb7hbM_ZeƩȐۀ9M)4P@ I-.QpN-*LLN,I-V020400)f(p4400 E*M,t2A iE `0Ģb9q^I@dal``%k5h( 1痥d2.E%`! 7X1=5/$*lbfj`hajaii$ntq44stt4t62t011aSp3hb7hbM_ZeƩȐۀ9M)4P@ I-.QpN-*LLN,I-V020400)f(p4400 E*M,t2A iE `0Ģb9q^I@dal``%k5h( 1痥d2.E%`! 7X1=5/$*lbfj`hajaii$ntq44stt4t6a2t01131v35hbTB=FV&F~8S##% ?7&+Xᶌ?qqp:8͎+}9XZON>.X٭f/;̞dEKA{ }͟'\̳y3Gr^U+Ek{t@2߫KONV_sz_Wc8,%^aUaдjOg9k*ne[aOU]T$=1ߍG׷m,쎮)mjs,6߼yDoC&fFML ZVC "<\iĒNh|,b,"}osJ' 6h|gicxӠڂ /dXfeS(r$bSg Rjd$a)tHBҮN~)LN>O$ۆ'#a$Rtf~IyEơh12p0Y#bԔ|D'4A2q@#4t򋋑3`w=c ,Rss^JYX催*cƑa W`coM,DSfƿwIe2^4ؘRÉLEZ77|Acz=\'Z"euew}>j6ۓWDSXCaƇGD,1V2۠wbƊW%}[f&z*e]0_/ _k]⥮"S̅׳Jemk>]9pr)~|w"Ma q Spa3hb30hbXhƩȐۀ9M)4P@ I-.QpN-*LLN,I-V020400)f(p4400 E*M,t2A iE `0Ģb9q^KcCKS3(q^C#$A)$ n_ZWʸ$eD 2`ԼJC)S!&#?'%H!3̠Q 9tY\LM [aSp3hb30hbXhƩȐۀ9M)4P@ I-.QpN-*LLN,I-V020400)f(p4400 E*M,t2A iE `0Ģb9q^KcCKS3(q^C#$A)$ n_ZWʸ$eD 2`ԼJC)S!&#?'%H!3̠Q 9tYa\LM [.d]g-c,e[]N"#JƜ";Zz*Ȭ&{R% |/(s?ϭ[){UT?a\힆՝3Q|Q3ՐГ]`nj, 5D>,Y_3[װؗٽ5wAKU?:[nKyw6FR.U) _d/$ۆ'#$Rtf~IyEơka,Mf@#wS5;;l/,..MM G@yA$th8"IAC@'(I< 1:p3!59'1\//8p#U@W`G}k_uMz/ً %a+OU7표Qw–oN^֏]vJEa^Y9{e8yj +X|GD<>%r㟻a͏7IM^Ƚk,FkꥁO]!YO9W'>1Nl<{@J9#. ?C3#7r6~=r]oڜ䴾ɹ.G _7~iÊ?S4q~O _/@cOpenSC-0.26.1/src/tests/fuzzing/corpus/fuzz_pkcs15_encode/9ad3fc3cb11967be927bad9263d326783c450e37000066400000000000000000000070711474147347300307270ustar00rootroot00000000000000;}1emmmmmmmma o a o a4a46a ay@pPr6X,!!yyyyyyyy00`0`000yyaya l@QABRCa a qp]r&aax3hb7hb1A߀Sͣ;/##++A!'s( 0Sh%_ZWbcfvwAx{x|.. ^! Ύr&F`%khZZFSv/4P@ ? =_ OW=CƖab_e$;wd۰"Q[Ś/ ,}UN=vasɚf~|;{%7n 9;b>:,w?ް[xi|=]ƛ*62] 7Umő/wnxҾe-qנ@Ye`4  U&QrB&'z @ YltqaFII~rQ^^f^Jfq^nf~zj P?FR88 dX x 8<S9 dA|,ba,"\ag}7^٥pt:6m6`bgdd)2(0 ڌ83=۔ ˊQ29E@@Ő=s##\'9YIo]WjA)Fߋ94? 'zW?W0!.)SzϛN\*rk)VCwMctҹ-)B*B,yL-@a^/%8<*oq<}͕[h'J6.kCvVEx᭨ޱ=-Vys}qq=iq'*2W5U8@tVWVYb;7|N{b2758b1e-508a-1275-92da-98506be00000}a l@QABTCa a qpr&aax3hbaܰƀSͣ;/##++A!'s( 0Sh%_ZWbcfvwAx{Hx".. ^! > Ύr&F`%khZZFS ^hE!A~AzAz@[͍-- ##+sc/Ac'Sc#eHJ)-TxAnTo aXEg%.yhRU֬wr= hvH<)ӆ3y|ŋ֭3)gq6p4_`2J_?(G//D/%8Q/73G?=(f%U#P<)pLiH\gdv ,l maLܜ q>1*Go0}n9PѠC Y * `|6+<+L>=9mJPs ˊQ4SQ((/ȡ(5%#D/9?w8&s&<|Jײ,"B09Y Y@ @$ʢdd L3ł똛V$/O|v0ؾ~lgDqvMohHaRxN]+{,]f|NyIgW^|&v8NFޕqBg"Nd?lʦQ{_ۼkWm ?>to\̹~vI9\?Sf1h{쳋m T.ߓ2U69O2w_+C'gEnjNު[Ulsl{c75bbada-529a8-175b-92dc-98506be00000}a l@QABWCa a qpr&aax3hb5hb6][kƩ ɐۀ9M)4P@  Sp/K-M+11p 3 = e A=h$!l†>_ Y+G׸Ϫ/;.vgv6h&fFMM@*ATIO>$xu @ Y  .n())O.,K,NOO-cIO@ S@W| 03*BV߀׀3̓a1@$""F㝹&ӄQwlOg4hlcf0vFF* 7߀ 3OncԜ<b#!d3i-)-K-r(JMH,K[YN!ɜ,,UgNtmZɥX iL2'xx?讏x'6|0t?9mGS|ѬRb,7QU͋aNLiIx69NOm;̻{"{ 7^2)G.|+vt\U.qt}RS.C&69>;?JsǞ8=F}z-;r:sLtm4~1sr;+}75>!)gRZ J:]tNc{d9857516-0054-1985-92de-98506be00000}cOpenSC-0.26.1/src/tests/fuzzing/corpus/fuzz_pkcs15_encode/b2b75c07a2c427c15ecd40ce47a9814279745b7d000066400000000000000000000066631474147347300307240ustar00rootroot00000000000000;ۖ1dwmmmmmmmma o a o a,a,.aay PP(XC!!yyyyyyyyyyya l@QAB(Ca a qpMr&auax3hbnt3#3c/VGw^FFVV(CnN6P6a`C 1K?T/XO=,(/75Đǀ $- 45yr ΎFr&@&DFƦQed؝n7glqOc{hpHhA|P`de`ne0hdjldخxߍ8Zn:Mn?b2擲a~[YcȐ[b5])SeN>qnSOq̭-דfx+X8L҂<*goZ:%>^wI u 5 gdv@WfT30}[Ȃ𱈱LsgFYn B>X_ 7gBc*#7H\t6+}̐aD=0U }$k8ـ) 7`j3cXqfzizp݌mJPzyyzEe(%j&FF" }z!귙|_q7WϻOJZY0L:Tub~n+ uoy~N]HnRr;˶F<<K{16862aca-020a-1686-ab5e-bd79d4220000}a l@QAB(Ca a qpr&aax3hb)7hb_Ĥ*oƩ ۀ9M)4P@  Sp/K-M+11p 3 = $ArX020724v2562sD49Gc6f\\9gKV1a=җG߲YZ$EƝӺj㮃C#>3 ϗpH!@uޙG*h|ł蜞`~=o71.7hb\l,?#f& 2 0DJJ7LLSUjϾb3)cc4{W7B#*Mtܨvtm8eX x 8<S9 <@, XgX'aed'f觧쁱jd $1!q 2IE@K\feegf lAJY LsJRS2Ks!@@4bdd0(3p ,83=0)AMO..+3+J.+F1-W17122[YkÓj}in6xڈi@axŽ-y߿ol+^霵i#a3- <~꼸d:ӏ7tdXAR[;ݗgH]}OΠ?-Ok0%5b>+lrX020724v2562S-T6lº-2rq ӻw+6zI2E6aM6*5-6Sk,UgF~O>#`wSnR櫜Gϥt%:nnCչwB[Uq8ygRfem.21320.nb1hb0zF? 3,/"f (SPXk VfdHIɡ iVWl|,b,"vGmf~núU`75fLh`Leb4a˳8.^`fQRR`\YYZ&U#_ N@$[8ـ鏝 7`jcjqfznc0)AMO..+3+J.+F1-+07122zFIN3O*2W#cپ;+| ќc6u[dr^et?3;v~U7,3ق/CMIWy \;Sf ~c{Lk.[o7PvRYpv5zR\{3a396a04-2962-1a39-ab62-bd79d4220000}cOpenSC-0.26.1/src/tests/fuzzing/corpus/fuzz_pkcs15_encode/cb50689bf49ccb45a2af690848517305dcf1e429000066400000000000000000000061211474147347300307230ustar00rootroot00000000000000;1Yubikey4jjjmmmaOyO ~O a _/@~O _/@ ~O a _/@~O _/@~O _/@j~O _/@ S;09a57s9shX!B!8B4SM%B)&8520300101>?S;09s9shX!B!8B4SM%B)&8520300101> SVpMa0I010  *H  0C10U Example10U Example Test10U Example Test CA0 190703094638Z 220329094638Z0310 U jjelen10 U jjelen10 U jjelen0Y0*H=*H=ḆZ\ qOӐSY^̟` 3W)m˿ naSVpM0I010  *H  0C10U Example10U Example Test10U Example Test CA0 190703094638Z 220329094638Z0310 U jjelen10 U jjelen10 U jjelen0Y0*H=*H=ḆZ\ qOӐSY^̟` 3W)ma˿ n){ja$rtF'ã#0!0U#0rv pZ{D<ӡ=0  *H  AۓʅHFE\_<ˈ4B7ٜ7HE-r==>cmUoDՓzQ|ylM|d^(M 9 fTF %GCt;+Ip݋P0Du?KI? fo҆$.B9D|a]_2˫ؽJcDSf}%AS+},AӉv< HuЪOdɹ4|~O4yD'L#+va "K[;8דqb,`G.CMQ#4H*O6DL=C'7yaSp00 ]ό_0  *H  010U SSH key0 171114115934Z 181114115934Z010U SSH key0"0  *H 0 ט>< HuЪOdɹ4|~O4yD'L#+va "K[;8דqb,`G.CMQ#4H*O6DL=Ca'7yhɿ6E mcȸ U.HuS0i GewjģVQ28Vk"#GC Pܰ@No(y*+b3VNKCʧo8"xkKUU+ZjThC,Ȕ ̂dA0  *H  q Slpca0_0 ;ˊ`0 *H=010U SSH test key0 190311081112Z 200310081112Z010U SSH test key0v0*H=+"b|)<éygp,d7/{ΚRUV&%^-ʠtim- x^TYYLQEv RN:R0 *H=h0eaiSlpc0_0 ;ˊ`0 *H=010U SSH test key0 190311081112Z 200310081112Z010U SSH test key0v0*H=+"b|)<éygp,d7/{ΚRUV&%^-ʠtim- x^TYYLQEv RN:R0 *H=arth0e0?jnFƃ˝YIlˤ\/(ηķMe,`1؈ Ju>F7Mub%^tm}i{èC>,1uq Sp0a00  *H 00 000001010000Z 000001010000Z000  *H 0{lh5,PS9/|EtNAR7 ;daR #!JCr#`[HI⊮<B8^AuR*> JxWHQ_TtTh\h0  *H qSp000  *H 00 000001010000Z 000001010000Z000  *H 0{lh5,PS9/|EtNAR7 ;daR #!JCr#`[HI⊮<B8^AuR*> JxWHQ_TtTh\h0  *H q~O _/@cOpenSC-0.26.1/src/tests/fuzzing/corpus/fuzz_pkcs15_encode/de913ba454f894cfc38a16dd122ad673d32ac480000066400000000000000000000026171474147347300310510ustar00rootroot00000000000000;1E%VWSC6508mz0&@@GSNPBP)xcf`cJIK6d L@A83=/3/]!;R!-H ()nlz3#/VGw^FFnV/CuU6P.dԔ\Ң̒J\[}Dуe?elճV,js["7&~v2[M]w |o45\Y%b9_fm/YR\2gs1֮tä)1=)7,ez1991;.|k7ġ^$"o;yncFVipOZaĦ a!C%̸AY)&r$0Ud蕩, 3KLuťsllV?ݒInco[ꍦϋ|twb]ɡֿBɏzch1rIӖ_k';2VfgoƝSQz,ְzyTXPyg<w}Vص]wp1LŪ@t{÷-\qSpY(T2EGY'i7Žӻs*"= -Kk8{RfҲЫ3axp6HyxF&qLRr*ּe3*V{8OZ`b],AفUKbzow_s@{k=„Eo |tx: ^-* 3w%T%O8egmϐڿrS={'OݱbMN32Ll?}++zMA&`0S 7!e*fOpenSC-0.26.1/src/tests/fuzzing/corpus/fuzz_pkcs15_reader/000077500000000000000000000000001474147347300234135ustar00rootroot00000000000000OpenSC-0.26.1/src/tests/fuzzing/corpus/fuzz_pkcs15_reader/741a0aae7b5b08c0ad2822ede5b3364302b28b31000066400000000000000000000051331474147347300307350ustar00rootroot00000000000000;1ESCE7  FBjjjjmmjjjjo ymo yd Ay Py `y By Cy DyE CID_o y@ QAB C QAB C QAB C QAB C QAB C QAB C QAB C QAB C QA B C o y@ QA B C QA B C QA B C QA B C QAB C QAB C QAB C RAB DRAB Do y@ QA B C QA B C QA B C QA B C QAB C QAB C QAB C RAB DRAB Dr(qp,gd{EC65ECE3-9D4F-46DC-8B78-07541319A0E1}x3hb`|c3#S/VGw^FFnV$CnN6P6a`C 1K?T/XO=,(/75Đǀ $- 45E\] ;*l6ga)=ٟt/czͣ^;!UQy?~,6>kOzqIM>ˣ,7-]&4ڛ͎PpjY߈O·56=QOWThu>O(he7EL~dCAK/͊wŎERxdgx:^+eӐsvFn}rN"}CSf6Hm-NժM_R71Ty`P*AyO7)u/ޛw|KIY>1U>_nUf{cZ6m6FVVvFFB|[߀0J_?(G//D/%8Q/73G83=O@%81\/9@M =?@/8@($>03VE@fPb1I 886gMjNbf^^:0!%9&f怌^ d.ļ@@Vҳ 7gBc*##H\ ,0j3 @,PA6GC7n'ϖL_jJs.XP (O>{=x]V_G},M +y]]  64iG[}b+Ɛ2 ˵ՊzjJKcƢSSkBz͜:aN;I'n3c߾krϫ}U:כ5'ola_UUΥgX5,[_~m /ۖyE+Y{T[Yvxkb```b&&F Il̡,lLb 0^{~YjQ^nj^!H[%i(k 1 (xy8+x(+8;3220T*]_Hs܋R+R, ,169CSJynZ#3P!e:Dxcbd`P````?x=S'ܴxGf3rC26;9+t T#Ez^yzf .@c*l̡,lLb 0^{~YjQ^nj^!H[%i(l q99:HE ^=۴^-:vւɊb|,w|UشmªSz:.?-^DG0vC~Xl| | 0?PD 0?PDA 0?PDQ 0?PDq 0?PD*o%P2240*QP Volkswagen AG VW PKI Cardrd$oD 0F0 Card PIN0,0* 00000000000000Z0?0:0 Signature PIN00L 0?oDl/080 Digital Signature0 40 0?P0:0 Non Repudiation0 3@0 0?P0<0 Encryption0aD00 ?P0<0 Encryption0bD00 ?P0<0 Encryption0cD00 ?P0<0 Encryption0dD00 ?P0<0 Encryption0et00 ?PvoDA/0/0 Digital Signature@0 0 0?PEA0-0 Non Repudiation@03 0 0?PEC0/0 Encryption@0a00?PEt000 Encryption@0b00?PEt000 Encryption@0c00?PEt Ȁ000 Encryption@0d00?PEt000 Encryption@0e00?PEtoDQ/060@0225118030566055813900?PEtl080 @0468135259511316737500?PEt"q070 @0818677575340868483800?PEsi090 @0-103714765826740230900?PEsiioEA /00J0  *H  0\10 &,dcom10 &,dvwg10 &,dVWPKI10UVW-QS-CA-OTHR-010 161021074212Z 201021074212Z0G10 &,dvwg100.U'Musterfrau Maike VWPKI FB247F21CE25B7BE0"0  *H 0 qhڙ+h!#fN;_VGa]S, Ր0NJ} gyӡࢿ򮱓8e<3S="SԈpZ=hr 5 9F UCЙ˹O4k4Kkѓ<2=8^rh)Т-y[v.fhtoJ쬋 3~L:#@n[ ݻA2Y5 w # {00PUI0G%extern.maike.musterfrau@volkswagen.de +7 VWMUSTE@vw.vwg03U%,0*+++ +70U# 0 K%Q+:'r0H+<0:08+0,http://qocsp.volkswagen.de/VW-QS-CA-OTHR-01/0U 00+ [0U0031/-http://crl.volkswagen.de/VW-QS-CA-OTHR-01.CRL0JHFDldap://ldap.volkswagen.de/cn=VW-QS-CA-OTHR-01,dc=vwPKI,dc=vwg,dc=com0U Bf-*0U0*p" 11511250 U00  *H  t~ +Fy4Y{85`y>E{C)-a?]eTJY͏mcxFA82k쌧seKY] sG,pm8 [Y !껆K[zOP_!R`660mLj)zH֋/2KC` 钔T_ѹz?tՐ3 $qk&ṁNG-/uNX߿MxэgH!{5+XֵX̽iz(*8_"!im-[S2膇O$s9s qwX )T(UEz:@YxM66м鑍:`ZV611$റL8Q՞+n<3&1??!yC0Ry=v]rum] *C^O^EsZ0H+<0:08+0,http://qocsp.volkswagen.de/VW-QS-CA-ENCS-01/0U 00+ [ 0U0031/-http://crl.volkswagen.de/VW-QS-CA-ENCS-01.CRL0JHFDldap://ldap.volkswagen.de/cn=VW-QS-CA-ENCS-01,dc=vwPKI,dc=vwg,dc=com0U L܂*0U000U)0'%extern.maike.musterfrau@volkswagen.de0*p" 11511250 U00  *H  UTk+uq#EJhAp8 ZL d=2Uƍք9(A펺LOIG_YE̻:\Zt+*d:Ҷm:")Sna]#'Ə"(l9dElGAb?oŠg#@1אlW]݈]MUF\`'H{/ŒۢE |By_\Jz{EAsDWnoD ^@B#\ n8F{b=݁q(fҌQ!͵{F}walftmեGq/n`0A:4"T 4`c4KtjCH4{Ȅ~EgA؄$p܀T5-H~dy^-Eb&WLC33&e+X6(@7<_\3vŘMK8Ip;ns@{*4"zKMiz: zQT*o%Et*00ȠI0  *H  0\10 &,dcom10 &,dvwg10 &,dVWPKI10UVW-QS-CA-OTHR-010 160609131733Z 200609131733Z0G10 &,dvwg100.U'Musterfrau Maike VWPKI FB247F21CE25B7BE0"0  *H 0 O~\MƴJK}!RJӇj 9my]y_Щ-T/;ۈ74$Gjcf-I@dOq̡3,a^UGMexK)Г2й䪆]"gwzcP4a9px5UۥC>:rL̴ 柇gߥ2JnH\ \IJ{L zwCD=ȑ8\{C FY[W^{R000U)0'%extern.maike.musterfrau@volkswagen.de06U%/0-++ +7  +7 0U# 0 K%Q+:'r0H+<0:08+0,http://qocsp.volkswagen.de/VW-QS-CA-OTHR-01/0U 00+ [0U0031/-http://crl.volkswagen.de/VW-QS-CA-OTHR-01.CRL0JHFDldap://ldap.volkswagen.de/cn=VW-QS-CA-OTHR-01,dc=vwPKI,dc=vwg,dc=com0U ID_0U00*p" 11515880 U00  *H  ]GlTC_|µXL0=]aGcL;3P?J~](f@[)ޙCc ϡɬ:W]a0F'KJE;ݑWBGHqnd#8݌0r`=)"dxl9M}4*֫I5zF"]٥h$z[!\` p՜MPcq ,*p2ɕqUBOQ[1-@红{Dɛ ^fFk97lKACwx}τ2ٺ`u5!``dGGK:wTT9ފ iI;X{\4yڒYJ5S__(8}\eeBΧO qpz5,p*o%Et*00ˠI-0  *H  0\10 &,dcom10 &,dvwg10 &,dVWPKI10UVW-QS-CA-OTHR-010 160519142351Z 200519142351Z0G10 &,dvwg100.U'Musterfrau Maike VWPKI FB247F21CE25B7BE0"0  *H 0 M ћa&Gi)b6~)` X#ѯ'.Bg}țF{4doҬJqӏw>n*V{VצgNJczpVHSk>jAuw~vx[۳r) o=̞1;)j Xg"ٳU, Zn3j Mq0Ye7?0#'{=&y4#000U)0'%extern.maike.musterfrau@volkswagen.de06U%/0-++ +7  +7 0U# 0 K%Q+:'r0H+<0:08+0,http://qocsp.volkswagen.de/VW-QS-CA-OTHR-01/0U 00+ [0U0031/-http://crl.volkswagen.de/VW-QS-CA-OTHR-01.CRL0JHFDldap://ldap.volkswagen.de/cn=VW-QS-CA-OTHR-01,dc=vwPKI,dc=vwg,dc=com0U MP5 80U00*p"  10550003820 U00  *H  an{Hx/.K07mwW) x3bQ`TV2mxEuz\f W@v)VA\ϵ#]000U)0'%extern.maike.musterfrau@volkswagen.de06U%/0-++ +7  +7 0U# 0 K%Q+:'r0H+<0:08+0,http://qocsp.volkswagen.de/VW-QS-CA-OTHR-01/0U 00+ [0U0031/-http://crl.volkswagen.de/VW-QS-CA-OTHR-01.CRL0JHFDldap://ldap.volkswagen.de/cn=VW-QS-CA-OTHR-01,dc=vwPKI,dc=vwg,dc=com0U LbU10U00*p" 11504650 U00  *H  5cy7@_ v:E$l15-L#E}k?.KoIK&҅* =hJC|j`LW%c *6ydP4EkHgʥŒ1k| }qB[''r:BlOw5+MO8dq `JOeĿ& лu9xahŤZT {K9FBlTM-+2Ґ4-ʹhL}+?4U.gJEЇ (d]N_ 6`GEKtS r+5vfJ>УD' fSkWeٮN-QaPUv(zDL9Ul^iZ0ZX٫KB5޷tg\ӴfkrFYcMF_D/.>T TAy.̕'n`n 17/{ ']:0X:Zġ!bi641dTo<N4BK_' C00@U%907+++ +7  +7 0U# 0 KFql0H+<0:08+0,http://qocsp.volkswagen.de/VW-QS-CA-PROC-01/0U 00+ [0U0031/-http://crl.volkswagen.de/VW-QS-CA-PROC-01.CRL0JHFDldap://ldap.volkswagen.de/cn=VW-QS-CA-PROC-01,dc=vwPKI,dc=vwg,dc=com0U Cn0U00U)0'%extern.maike.musterfrau@volkswagen.de0 U00  *H  4}u1Xl;- +Nʿ|kdHO"9a3G:fܘ-"k(qH;12`iZ5|28:7DUl i7O-;f_9u3GKqbE+dAv=a* zY㤧pTѥ*ߕ7v_ak?3]$h؇etjČaيQ<5Xa7igCXHCL5z^ro|);~܍@!dH FEv%aԗ:8t!JR$tZ$U`*]'Ͽzfje(!~7Lp ,JݝQKW#1\7ERG|?5J^ ËQ :o!dF1 AY~Z. Cm?QE8S#?<[}咒+wO-RPcbP`>cJu|ي-EݜRuOy<5\?,_VJ8$:3]h\!G.?O$$k#kΗ}r̦!u0t#gH"P QL_2eJES0Q0U00U GY m}0U 00+ [0 U0  *H  E{Ri6hr%(/!w{M$s“r hܧPzg^R~&Pg8REk>W* fuF񳀓5moK I $wDZ*rWɼyp4jiVG'+ w9 - !_E]aPH⥢&AHQud$Q<¹Cu-uB*BvcK;ߴU(0GWT_nŐ],c[疢l҂Emf Vΰ+5;G_=^x:n˗%OH dm' wtHǛ494ʈ±ثeUq!wIfPEa4'0,L@ ~\f)+#p,g{*o%Et*0m0UB'0  *H  0\10 &,dcom10 &,dvwg10 &,dVWPKI10UVW-QS-CA-ROOT-010 130606100000Z 210806100000Z0\10 &,dcom10 &,dvwg10 &,dVWPKI10UVW-QS-CA-OTHR-010 0  *H  0Y9D8;m ĪU_}BݼWFyA Hf@NS7o^>u9iӫ 8O ),UֳA\ w+1z6^1x ]Cpip~>=PFD5f>la{h_*)z N =yX6J>%2OA ;Lz+döQ֛@ĊiL gm::Rۤ#߹fC7su{ܶ@v9B%JƤV4感ZSƶF*1nr% ޓ!QdfBw՘e#(~yhGX6ܥtɃ` 5ێOb5qo NsQ%EŎW~&E/B@tQL $@yh=WjL`hQ? [ BnSH2Ƀ oϝqTA9050U00U K%Q+:'r0&U 00U 0+ [0U# 0 GY m}06+*0(0&+0http://qocsp.volkswagen.de0 U0U0031/-http://crl.volkswagen.de/VW-QS-CA-ROOT-01.CRL0JHFDldap://ldap.volkswagen.de/cn=VW-QS-CA-ROOT-01,dc=vwPKI,dc=vwg,dc=com0  *H  Oa.V?brbX gx5-7b_mToЫjG@{3iҗ7lStOflH, 4QP7M~%+)*Ɣ8V)^*%&'rOUOz0L ˢS7>L+rRTIML9 vMS&S_!@zbrG^*5P ; o;8:Q(4O'o8q{g6BsӫuۭUyfFr ɭ  3?~o,srۮOw;f$Y}ǜd j9+~Na]BU(E{N?a_U-;c~tP]f3FTt,P dمVS5]@ѧ@-h9}vRh|,kk՟BC%C$O*{od J#&*o%Es*0e0MB%0  *H  0\10 &,dcom10 &,dvwg10 &,dVWPKI10UVW-QS-CA-ROOT-010 130606100000Z 210806100000Z0\10 &,dcom10 &,dvwg10 &,dVWPKI10UVW-QS-CA-ENCS-010 0  *H  0ږfVgX^a 䈤9rJO/qRFWTjπp B R7sqS/ϕw:]"_E~=(ݐR `g"wA}v2U{ -Ǒ㖈'ϙYmP\vd⼟Nϣ,$v9;b#鉌'bt?<΅ 'S\9yg^TKe/LASM'h,@-)}oTη"8maȔo@C=_ͣrC<kN^gTcv̎1D1ӠEnacKSFmF!n#RHn|]N#| P꾷7/)Sغ=165RewFAeb5* 3:cwټ:`VquAO:h!_?SB852Rlz{h;$&0z}9|0.mS#% lrcO&\ &J5SB9W10-0U00U L%>sZ0U 00+ [0U# 0 GY m}06+*0(0&+0http://qocsp.volkswagen.de0 U0U0031/-http://crl.volkswagen.de/VW-QS-CA-ROOT-01.CRL0JHFDldap://ldap.volkswagen.de/cn=VW-QS-CA-ROOT-01,dc=vwPKI,dc=vwg,dc=com0  *H  v/(bj$oP JcC{gzӑ\U?17; 3:P0L_4 8dE\yMFȓG Uv2 \LyKx(rZOY_)YE4>VMQcjR%d}-G|$OПb>([L@NɍnФ=IB/#5zTqeOVtÁLF<1UK2X ID-93QHjXx,VYM5WN%նO,Z]u50@{W,|Dֲ΄_WeېMd@ZѬ; H򊇐s|*r[3 k(%IdYҐHRvU;afn'k-⦏e 1z!qًH6 ]vR}C)&zCsl"+>*o%Es*0e0MI0  *H  0\10 &,dcom10 &,dvwg10 &,dVWPKI10UVW-QS-CA-ROOT-010 160606100000Z 210806100000Z0\10 &,dcom10 &,dvwg10 &,dVWPKI10UVW-QS-CA-SIGN-020 0  *H  0F+'D!Gh</'_J3ҵf/krC |4G:ur$CT1ANhݒP~7Xti\}G"„*c51νB7.X$s%E ;ScBYa Y!ɩ<|f`_ڗ{* #v/2*H'e=y$û釐Wdwd7C܊cw)%t>$& b4(Uf ōvJ4@I6|k{c $td|*VWb[wld$3iI;U}MY%-[1WGxNR͸+%T ~0a}/b{FrMf2. [}.'10-0U00U DTlVD0U 00+ [0U# 0 GY m}06+*0(0&+0http://qocsp.volkswagen.de0 U0U0031/-http://crl.volkswagen.de/VW-QS-CA-ROOT-01.CRL0JHFDldap://ldap.volkswagen.de/cn=VW-QS-CA-ROOT-01,dc=vwPKI,dc=vwg,dc=com0  *H  +L~ˠ- IU񫷞-J0c(e ؏@aVÜArDg ,lH(`# ]jUx@и$}rpz?LA!J@\9LKO:$ X+,5Ucwa=OO}ONLKe=WKseI:_ Ip=}uQtפX\*065XFb~諸{jc@J:;c8H[F)I'Q*Ə/TS(XzNG4bi0 *H 010  `He0 `He 000  *H  0r1 0 UUS10U Test Certificates 201010U Test CA100.U'Test RSA 2048-bit CA for Test PIV aS 0PX홒j١'DPC4520301231>0 *H 010  `He0 `He 000  *H  0r1 0 UUS10U Test Certificates 201010U Test CA100.U'Test RSA 2048-bit CA for Taest PIV Cards0 101001083000Z 301001083000Z0e1 0 UUS10U Test Government10U Test Department1"0 UTest PIV Content Signer 50"0  *H 0 ޷vIsS~1SasL%04dZ,$smt9Y,4p\Ta[Z;5JZ:( J % };5•_l$ N"_M(=AQEnmB;e 39%œ> U#؜4|iɰ=e`+S7ډ A44kܒ ufʢB֕$]اD y.T4һ00D+6020G+0;http://smime2.nist.gov/PIVaTest/CACertsIssuedToRSA2048CA.p7c0+0ldap://smime2.nist.gov/cn=Test%20RSA%202048-bit%20CA%20for%20Test%20PIV%20Cards,ou=Test%20CA,o=Test%20Certificates%202010,c=US?cACertificate;binary,crossCertificatePair;binary0(+0http://seclab7.ncsal.nist.gov0U00֠ӠІ,http://smime2.nist.gov/PIVTest/RSA2048CA.crlldap://smime2.nist.gov/cn=Test%20RSA%202048-bit%20CA%20for%20Test%20PIV%20Cards,ou=Test%20CA,o=Test%20Certificates%202010,c=US?certificateRevocationList;binary0U_aƾ+8l0U#0HDtW 0U 00  `He0U% 0 `He0U0  *H  Z Hd&QG2wK) ⡎ x ?Sr)cYdj XWIcΆC~"rC: hZÇR*oQ苵1#g xڸ,o9"sՍ@5eajfva$I팵cAYWkU׋;dʎW;NY5RGRhجMXN"r:;y0Kz1,`|̻.#cͮSI gT|10~0w0r1 0 UUS10U Test Certificates 201010U Test CA100.U'Test RSA 2048-bit CA for Test PIV Caards0  `He0 *H  1 `He0 *H  1 110403195841Z0/ *H  1" A.m6uv{3Fu0s`He1g0e1 0 UUS10U Test Government10U Test Department1"0 UTest PIV Content Signer 50  *H a Bj /UDr5 ~ZqbM ,D䲢YO` ۻ-K:ǫ45')XeN'R’ f$mhȈ;S%ŝE{K>WLSXEhACR=5 U/WaeXt Mb AGņXNK-9ga G"r" Spa3hbgڶҀSͣ;/##7+A!'s( 0Sh,%,Z\ZTXZ`d`h`(`R-Qhh`aV;UX&ea ΉE)r@ JkP& 1痥d2.E%`! 7X1=5/P@ TaのII-R 3hbTBFV&F~8S##1JaSp3hbgڶҀSͣ;/##7+A!'s( 0Sh,%,Z\ZTXZ`d`h`(`R-Qhh`aV;UX&ea ΉE)r@ JkP& 1痥d2.E%`! 7X1=5/P@ TaのII-R 3hbTBFV&F~8Sa##1JUj;/,ܐܧkk5;{]^S=K̗/lnfv}ԒԓdUuԣR3\%};(Ӓ\[},׶ۇ'>qiR|2rˏwڶӭ]_J ֮~4b}r44^Hsڃ#˝ߋmi~759ߗ'vȘ#8#ˁnu*Na A҅f21320.nb4hb652 0 Dylkӄ%gÏdXXDV]ԷYgf7RH9>Ӡ H^AMk //hЦQRR`_jY\_L=8&/PrvK.ikMyr-HPH J U4$J:0Ύ:p6R>fhllJ-ҙy>@WX'e%U 6#++;#a#KP&ffMΎ ۋ=KSSBP`lиɤHRI./.FH,m2pHMIL2K.΁{\32gaafbo0PUY d`F03qY,M"LLl,@iq2`eƕ , y 8!<6NFf`6H)d6\d9c , -M ,LJ R+s rRsq@ c[ "y-@"b朷3']usY-.m_ _faPҲY-e-ul_8~jp/:\Q 6ONw* YOOR^n§%gf3QDr'/o\H>u^@jݟ{6ye߸leKWO`ꏻl~Xt0{S+g?qJIV)Wf=|w/O]1ǥܙ2=''f>RIH_6gW3gz6 .3 y'1fVgd[)εJؠ H^AMk //hЦQRR`_jY\_L=8&/PrvK.ikMyr-HPH J U4$J:0Ύ:p6R>fhllJ-ҙy>@WX'e%U 6#++;#a#KP&ffMΎ ۋ=KSSBP`lиɤHRI./.FH,m2pHMIL2K.΁{\32gaafb;` +#66T`n dQ3PiTD/ Mr3sR+s rRsъCfP>wgȘė ߾A'/Eؤ(i;Tk(~U_6_۽%i#y+Vxhߛ+cUW)Gm5ax&[Z-D ߇O~aۓ#u>;iN1.Y0-XEK]+p.~x![ԃ/ \Ohx^O-L>uDc}oX+gҾ$P}Љw,ۛO q+y϶8̚@bWWX6߈^8gc5ҹ 4q Spa3hb30hbXZcƩȐۀ9M)4P@ I-.QpN-*LLN,I-V020400)f(p4400 E*M,t2A iE `0Ģb9q^I@dal``%k5(EuM`sRrSJ`2PԂĢD,㘞\i(c q0@d礤)Dx41*!#+s#?PᶾaSp3hb30hbXZcƩȐۀ9M)4P@ I-.QpN-*LLN,I-V020400)f(p4400 E*M,t2A iE `0Ģb9q^I@dal``%k5(EuM`sRrSJ`2PԂĢD,㘞\i(c q0@d礤)Dx41*!#+s#?Paᶾ7[מ|hľ-r?Տ[+uWe0CkWEP 0qҳ[_Xx{f~l~Cc١L`ذ} h[_CHS+"5+1SLX}'sו Y44h m:%%VŹFzy%ze3}`.gG䢜9)ؔ'ق5IX2A|`P%@A⠴_ 쨓g#i:ɶԲ| tuRf^bQ%0qh132a41E abLm|س45%${Lj\?С$ b$"Xh]& $s,?#f&Vq_Xq%y08fv] J}%z\nh␛Z[V29;Ą{/_U佥-8P?e@rl\߂o~tXKزԝgj>-Se^dƵNbO!bAEag3$}zwuզDŀ;/'y6חB:)MNsOR>vW'<5Inۂ/rK'y;7ݛyϜGel1x85&~_,҇o^??]|k>--"K[k7m^5;j u4q Spa3hb7hbM_ZeƩȐۀ9M)4P@ I-.QpN-*LLN,I-V020400)f(p4400 E*M,t2A iE `0Ģb9q^I@dal``%k5h( 1痥d2.E%`! 7X1=5/$*lbfj`hajaii$ntq44stt4t62t011aSp3hb7hbM_ZeƩȐۀ9M)4P@ I-.QpN-*LLN,I-V020400)f(p4400 E*M,t2A iE `0Ģb9q^I@dal``%k5h( 1痥d2.E%`! 7X1=5/$*lbfj`hajaii$ntq44stt4t6a2t01131v35hbTB=FV&F~8S##% ?7&+Xᶌ?qqp:8͎+}9XZON>.X٭f/;̞dEKA{ }͟'\̳y3Gr^U+Ek{t@2߫KONV_sz_Wc8,%^aUaдjOg9k*ne[aOU]T$=1ߍG׷m,쎮)mjs,6߼yDoC&fFML ZVC "<\iĒNh|,b,"}osJ' 6h|gicxӠڂ /dXfeS(r$bSg Rjd$a)tHBҮN~)LN>O$ۆ'#a$Rtf~IyEơh12p0Y#bԔ|D'4A2q@#4t򋋑3`w=c ,Rss^JYX催*cƑa W`coM,DSfƿwIe2^4ؘRÉLEZ77|Acz=\'Z"euew}>j6ۓWDSXCaƇGD,1V2۠wbƊW%}[f&z*e]0_/ _k]⥮"S̅׳Jemk>]9pr)~|w"Ma q Spa3hb30hbXhƩȐۀ9M)4P@ I-.QpN-*LLN,I-V020400)f(p4400 E*M,t2A iE `0Ģb9q^KcCKS3(q^C#$A)$ n_ZWʸ$eD 2`ԼJC)S!&#?'%H!3̠Q 9tY\LM [aSp3hb30hbXhƩȐۀ9M)4P@ I-.QpN-*LLN,I-V020400)f(p4400 E*M,t2A iE `0Ģb9q^KcCKS3(q^C#$A)$ n_ZWʸ$eD 2`ԼJC)S!&#?'%H!3̠Q 9tYa\LM [.d]g-c,e[]N"#JƜ";Zz*Ȭ&{R% |/(s?ϭ[){UT?a\힆՝3Q|Q3ՐГ]`nj, 5D>,Y_3[װؗٽ5wAKU?:[nKyw6FR.U) _d/$ۆ'#$Rtf~IyEơka,Mf@#wS5;;l/,..MM G@yA$th8"IAC@'(I< 1:p3!59'1\//8p#U@W`G}k_uMz/ً %a+OU7표Qw–oN^֏]vJEa^Y9{e8yj +X|GD<>%r㟻a͏7IM^Ƚk,FkꥁO]!YO9W'>1Nl<{@J9#. ?C3#7r6~=r]oڜ䴾ɹ.G _7~iÊ?S4q~O _/@cOpenSC-0.26.1/src/tests/fuzzing/corpus/fuzz_pkcs15_reader/9ad3fc3cb11967be927bad9263d326783c450e37000066400000000000000000000070711474147347300307340ustar00rootroot00000000000000;}1emmmmmmmma o a o a4a46a ay@pPr6X,!!yyyyyyyy00`0`000yyaya l@QABRCa a qp]r&aax3hb7hb1A߀Sͣ;/##++A!'s( 0Sh%_ZWbcfvwAx{x|.. ^! Ύr&F`%khZZFSv/4P@ ? =_ OW=CƖab_e$;wd۰"Q[Ś/ ,}UN=vasɚf~|;{%7n 9;b>:,w?ް[xi|=]ƛ*62] 7Umő/wnxҾe-qנ@Ye`4  U&QrB&'z @ YltqaFII~rQ^^f^Jfq^nf~zj P?FR88 dX x 8<S9 dA|,ba,"\ag}7^٥pt:6m6`bgdd)2(0 ڌ83=۔ ˊQ29E@@Ő=s##\'9YIo]WjA)Fߋ94? 'zW?W0!.)SzϛN\*rk)VCwMctҹ-)B*B,yL-@a^/%8<*oq<}͕[h'J6.kCvVEx᭨ޱ=-Vys}qq=iq'*2W5U8@tVWVYb;7|N{b2758b1e-508a-1275-92da-98506be00000}a l@QABTCa a qpr&aax3hbaܰƀSͣ;/##++A!'s( 0Sh%_ZWbcfvwAx{Hx".. ^! > Ύr&F`%khZZFS ^hE!A~AzAz@[͍-- ##+sc/Ac'Sc#eHJ)-TxAnTo aXEg%.yhRU֬wr= hvH<)ӆ3y|ŋ֭3)gq6p4_`2J_?(G//D/%8Q/73G?=(f%U#P<)pLiH\gdv ,l maLܜ q>1*Go0}n9PѠC Y * `|6+<+L>=9mJPs ˊQ4SQ((/ȡ(5%#D/9?w8&s&<|Jײ,"B09Y Y@ @$ʢdd L3ł똛V$/O|v0ؾ~lgDqvMohHaRxN]+{,]f|NyIgW^|&v8NFޕqBg"Nd?lʦQ{_ۼkWm ?>to\̹~vI9\?Sf1h{쳋m T.ߓ2U69O2w_+C'gEnjNު[Ulsl{c75bbada-529a8-175b-92dc-98506be00000}a l@QABWCa a qpr&aax3hb5hb6][kƩ ɐۀ9M)4P@  Sp/K-M+11p 3 = e A=h$!l†>_ Y+G׸Ϫ/;.vgv6h&fFMM@*ATIO>$xu @ Y  .n())O.,K,NOO-cIO@ S@W| 03*BV߀׀3̓a1@$""F㝹&ӄQwlOg4hlcf0vFF* 7߀ 3OncԜ<b#!d3i-)-K-r(JMH,K[YN!ɜ,,UgNtmZɥX iL2'xx?讏x'6|0t?9mGS|ѬRb,7QU͋aNLiIx69NOm;̻{"{ 7^2)G.|+vt\U.qt}RS.C&69>;?JsǞ8=F}z-;r:sLtm4~1sr;+}75>!)gRZ J:]tNc{d9857516-0054-1985-92de-98506be00000}cOpenSC-0.26.1/src/tests/fuzzing/corpus/fuzz_pkcs15_reader/b2b75c07a2c427c15ecd40ce47a9814279745b7d000066400000000000000000000066631474147347300307310ustar00rootroot00000000000000;ۖ1dwmmmmmmmma o a o a,a,.aay PP(XC!!yyyyyyyyyyya l@QAB(Ca a qpMr&auax3hbnt3#3c/VGw^FFVV(CnN6P6a`C 1K?T/XO=,(/75Đǀ $- 45yr ΎFr&@&DFƦQed؝n7glqOc{hpHhA|P`de`ne0hdjldخxߍ8Zn:Mn?b2擲a~[YcȐ[b5])SeN>qnSOq̭-דfx+X8L҂<*goZ:%>^wI u 5 gdv@WfT30}[Ȃ𱈱LsgFYn B>X_ 7gBc*#7H\t6+}̐aD=0U }$k8ـ) 7`j3cXqfzizp݌mJPzyyzEe(%j&FF" }z!귙|_q7WϻOJZY0L:Tub~n+ uoy~N]HnRr;˶F<<K{16862aca-020a-1686-ab5e-bd79d4220000}a l@QAB(Ca a qpr&aax3hb)7hb_Ĥ*oƩ ۀ9M)4P@  Sp/K-M+11p 3 = $ArX020724v2562sD49Gc6f\\9gKV1a=җG߲YZ$EƝӺj㮃C#>3 ϗpH!@uޙG*h|ł蜞`~=o71.7hb\l,?#f& 2 0DJJ7LLSUjϾb3)cc4{W7B#*Mtܨvtm8eX x 8<S9 <@, XgX'aed'f觧쁱jd $1!q 2IE@K\feegf lAJY LsJRS2Ks!@@4bdd0(3p ,83=0)AMO..+3+J.+F1-W17122[YkÓj}in6xڈi@axŽ-y߿ol+^霵i#a3- <~꼸d:ӏ7tdXAR[;ݗgH]}OΠ?-Ok0%5b>+lrX020724v2562S-T6lº-2rq ӻw+6zI2E6aM6*5-6Sk,UgF~O>#`wSnR櫜Gϥt%:nnCչwB[Uq8ygRfem.21320.nb1hb0zF? 3,/"f (SPXk VfdHIɡ iVWl|,b,"vGmf~núU`75fLh`Leb4a˳8.^`fQRR`\YYZ&U#_ N@$[8ـ鏝 7`jcjqfznc0)AMO..+3+J.+F1-+07122zFIN3O*2W#cپ;+| ќc6u[dr^et?3;v~U7,3ق/CMIWy \;Sf ~c{Lk.[o7PvRYpv5zR\{3a396a04-2962-1a39-ab62-bd79d4220000}cOpenSC-0.26.1/src/tests/fuzzing/corpus/fuzz_pkcs15_reader/cb50689bf49ccb45a2af690848517305dcf1e429000066400000000000000000000061211474147347300307300ustar00rootroot00000000000000;1Yubikey4jjjmmmaOyO ~O a _/@~O _/@ ~O a _/@~O _/@~O _/@j~O _/@ S;09a57s9shX!B!8B4SM%B)&8520300101>?S;09s9shX!B!8B4SM%B)&8520300101> SVpMa0I010  *H  0C10U Example10U Example Test10U Example Test CA0 190703094638Z 220329094638Z0310 U jjelen10 U jjelen10 U jjelen0Y0*H=*H=ḆZ\ qOӐSY^̟` 3W)m˿ naSVpM0I010  *H  0C10U Example10U Example Test10U Example Test CA0 190703094638Z 220329094638Z0310 U jjelen10 U jjelen10 U jjelen0Y0*H=*H=ḆZ\ qOӐSY^̟` 3W)ma˿ n){ja$rtF'ã#0!0U#0rv pZ{D<ӡ=0  *H  AۓʅHFE\_<ˈ4B7ٜ7HE-r==>cmUoDՓzQ|ylM|d^(M 9 fTF %GCt;+Ip݋P0Du?KI? fo҆$.B9D|a]_2˫ؽJcDSf}%AS+},AӉv< HuЪOdɹ4|~O4yD'L#+va "K[;8דqb,`G.CMQ#4H*O6DL=C'7yaSp00 ]ό_0  *H  010U SSH key0 171114115934Z 181114115934Z010U SSH key0"0  *H 0 ט>< HuЪOdɹ4|~O4yD'L#+va "K[;8דqb,`G.CMQ#4H*O6DL=Ca'7yhɿ6E mcȸ U.HuS0i GewjģVQ28Vk"#GC Pܰ@No(y*+b3VNKCʧo8"xkKUU+ZjThC,Ȕ ̂dA0  *H  q Slpca0_0 ;ˊ`0 *H=010U SSH test key0 190311081112Z 200310081112Z010U SSH test key0v0*H=+"b|)<éygp,d7/{ΚRUV&%^-ʠtim- x^TYYLQEv RN:R0 *H=h0eaiSlpc0_0 ;ˊ`0 *H=010U SSH test key0 190311081112Z 200310081112Z010U SSH test key0v0*H=+"b|)<éygp,d7/{ΚRUV&%^-ʠtim- x^TYYLQEv RN:R0 *H=arth0e0?jnFƃ˝YIlˤ\/(ηķMe,`1؈ Ju>F7Mub%^tm}i{èC>,1uq Sp0a00  *H 00 000001010000Z 000001010000Z000  *H 0{lh5,PS9/|EtNAR7 ;daR #!JCr#`[HI⊮<B8^AuR*> JxWHQ_TtTh\h0  *H qSp000  *H 00 000001010000Z 000001010000Z000  *H 0{lh5,PS9/|EtNAR7 ;daR #!JCr#`[HI⊮<B8^AuR*> JxWHQ_TtTh\h0  *H q~O _/@cOpenSC-0.26.1/src/tests/fuzzing/corpus/fuzz_pkcs15_reader/de913ba454f894cfc38a16dd122ad673d32ac480000066400000000000000000000026171474147347300310560ustar00rootroot00000000000000;1E%VWSC6508mz0&@@GSNPBP)xcf`cJIK6d L@A83=/3/]!;R!-H ()nlz3#/VGw^FFnV/CuU6P.dԔ\Ң̒J\[}Dуe?elճV,js["7&~v2[M]w |o45\Y%b9_fm/YR\2gs1֮tä)1=)7,ez1991;.|k7ġ^$"o;yncFVipOZaĦ a!C%̸AY)&r$0Ud蕩, 3KLuťsllV?ݒInco[ꍦϋ|twb]ɡֿBɏzch1rIӖ_k';2VfgoƝSQz,ְzyTXPyg<w}Vص]wp1LŪ@t{÷-\qSpY(T2EGY'i7Žӻs*"= -Kk8{RfҲЫ3axp6HyxF&qLRr*ּe3*V{8OZ`b],AفUKbzow_s@{k=„Eo |tx: ^-* 3w%T%O8egmϐڿrS={'OݱbMN32Ll?}++zMA&`0S 7!e*fOpenSC-0.26.1/src/tests/fuzzing/corpus/fuzz_pkcs15_tool/000077500000000000000000000000001474147347300231265ustar00rootroot00000000000000OpenSC-0.26.1/src/tests/fuzzing/corpus/fuzz_pkcs15_tool/580ffba4a6c4d24100dd3dc11ab0014be3de7a6b000066400000000000000000000073371474147347300307520ustar00rootroot00000000000000-k-C-s;ۖ1dwmmmmmmmma o a o a,a,.aay PP(XC!!yyyyyyyyyyya l@QAB(Ca a qpMr&auax3hbnt3#3c/VGw^FFVV(CnN6P6a`C 1K?T/XO=,(/75Đǀ $- 45yr ΎFr&@&DFƦQed؝n7glqOc{hpHhA|P`de`ne0hdjldخxߍ8Zn:Mn?b2擲a~[YcȐ[b5])SeN>qnSOq̭-דfx+X8L҂<*goZ:%>^wI u 5 gdv@WfT30}[Ȃ𱈱LsgFYn B>X_ 7gBc*#7H\t6+}̐aD=0U }$k8ـ) 7`j3cXqfzizp݌mJPzyyzEe(%j&FF" }z!귙|_q7WϻOJZY0L:Tub~n+ uoy~N]HnRr;˶F<<K{16862aca-020a-1686-ab5e-bd79d4220000}a l@QAB(Ca a qpr&aax3hb)7hb_Ĥ*oƩ ۀ9M)4P@  Sp/K-M+11p 3 = $ArX020724v2562sD49Gc6f\\9gKV1a=җG߲YZ$EƝӺj㮃C#>3 ϗpH!@uޙG*h|ł蜞`~=o71.7hb\l,?#f& 2 0DJJ7LLSUjϾb3)cc4{W7B#*Mtܨvtm8eX x 8<S9 <@, XgX'aed'f觧쁱jd $1!q 2IE@K\feegf lAJY LsJRS2Ks!@@4bdd0(3p ,83=0)AMO..+3+J.+F1-W17122[YkÓj}in6xڈi@axŽ-y߿ol+^霵i#a3- <~꼸d:ӏ7tdXAR[;ݗgH]}OΠ?-Ok0%5b>+lrX020724v2562S-T6lº-2rq ӻw+6zI2E6aM6*5-6Sk,UgF~O>#`wSnR櫜Gϥt%:nnCչwB[Uq8ygRfem.21320.nb1hb0zF? 3,/"f (SPXk VfdHIɡ iVWl|,b,"vGmf~núU`75fLh`Leb4a˳8.^`fQRR`\YYZ&U#_ N@$[8ـ鏝 7`jcjqfznc0)AMO..+3+J.+F1-+07122zFIN3O*2W#cپ;+| ќc6u[dr^et?3;v~U7,3ق/CMIWy \;Sf ~c{Lk.[o7PvRYpv5zR\{3a396a04-2962-1a39-ab62-bd79d4220000}a l@P AB0a6a68 r) r) r)aaΠy{1545744a-0154-1545-ab5d-bd79d4220000}Πy{2960acf6-200c-1960-ab5f-bd79d4220000}Πy{3903639c-0361-1903-ab61-bd79d4220000}OpenSC-0.26.1/src/tests/fuzzing/corpus/fuzz_pkcs15_tool/5ccb89aff2634fc168e0758cb2005d6dcf0398bc000066400000000000000000000051351474147347300306630ustar00rootroot00000000000000-k-C-s;1ESCE7  FBmmmmmmmmmo ymo yd Ay Py `y By Cy DyE CID_o y@ QAB C QAB C QAB C QAB C QAB C QAB C QAB C QAB C QA B C o y@ QA B C QA B C QA B C QA B C QAB C QAB C QAB C RAB DRAB Do y@ QA B C QA B C QA B C QA B C QAB C QAB C QAB C RAB DRAB Dr(qp,gd{EC65ECE3-9D4F-46DC-8B78-07541319A0E1}x3hb`|c3#S/VGw^FFnV$CnN6P6a`C 1K?T/XO=,(/75Đǀ $- 45E\] ;*l6ga)=ٟt/czͣ^;!UQy?~,6>kOzqIM>ˣ,7-]&4ڛ͎PpjY߈O·56=QOWThu>O(he7EL~dCAK/͊wŎERxdgx:^+eӐsvFn}rN"}CSf6Hm-NժM_R71Ty`P*AyO7)u/ޛw|KIY>1U>_nUf{cZ6m6FVVvFFB|[߀0J_?(G//D/%8Q/73G83=O@%81\/9@M =?@/8@($>03VE@fPb1I 886gMjNbf^^:0!%9&f怌^ d.ļ@@Vҳ 7gBc*##H\ ,0j3 @,PA6GC7n'ϖL_jJs.XP (O>{=x]V_G},M +y]]  64iG[}b+Ɛ2 ˵ՊzjJKcƢSSkBz͜:aN;I'n3c߾krϫ}U:כ5'ola_UUΥgX5,[_~m /ۖyE+Y{T[Yvxkb```b&&F Il̡,lLb 0^{~YjQ^nj^!H[%i(k 1 (xy8+x(+8;3220T*]_Hs܋R+R, ,169CSJynZ#3P!e:Dxcbd`P````?x=S'ܴxGf3rC26;9+t T#Ez^yzf .@c*l̡,lLb 0^{~YjQ^nj^!H[%i(l q99:HE ^=۴^-:vւɊb|,w|UشmªSz:.?-^DG0vC~Xl| |0 *H 010  `He0 `He,0(00  *H  0x1 0 UUS10U Test Certificates 201010U Test CA1604U-Test PIV-I RSA 2048-bit CAaaS 209s9shYfyP74Q"A_^c520301231>0 *H 010  `He0 `He,0(00  *H  0x1 0 UUS10U Test Certificates 201010U Test CA1604U-Test PIV-I RSA 2048-bit CAa for Test PIV Cards0 101001083000Z 301001083000Z0g1 0 UUS10U Test Government10U Test Department1$0"UTest PIV-I Content Signer 10"0  *H 0 M/= @lCvu0sObhcov4<5%O!AT4V TCaFlcF+‘ yKn3Zo#TU_*sՅ?ggP^ͻ1(¶:^ܿ*% h J7'ЗdA& M$YldS46{27O91TGydoMApAbyuJtOsw00P+B0>0K+0?http://smime2.nista.gov/PIVTest/CACertsIssuedToRSA2048PIVICA.p7c0+0ldap://smime2.nist.gov/cn=Test%20PIV-I%20RSA%202048-bit%20CA%20for%20Test%20PIV%20Cards,ou=Test%20CA,o=Test%20Certificates%202010,c=US?cACertificate;binary,crossCertificatePair;binary0(+0ahttp://seclab7.ncsl.nist.gov0U00⠁ߠ܆0http://smime2.nist.gov/PIVTest/RSA2048PIVICA.crlldap://smime2.nist.gov/cn=Test%20PIV-I%20RSA%202048-bit%20CA%20for%20Test%20PIV%20Cards,ou=Test%20CA,o=Test%20Certificates%202010,c=US?certificateRevoacationList;binary0U'xoT;?7#Lo L0q,\NQ"K+ܡn<8ѣTkFk+i\鞹5{橭P_R;2:;Ec4[z}fct+чā@-82pVa舔={^28^+쌞KEd܇B C%ˌkVp]g:0NI0E0U#0I.|7(,30UG<⪐.­vXhN0U00⠁ߠ܆0http://smime2.nist.gov/PIVTest/RSA2048PIVICA.crlldap://smime2a.nist.gov/cn=Test%20PIV-I%20RSA%202048-bit%20CA%20for%20Test%20PIV%20Cards,ou=Test%20CA,o=Test%20Certificates%202010,c=US?certificateRevocationList;binary0P+B0>0K+0?http://smime2.nist.gov/PIVTest/CACertsIssuedToRSA2048PIVICA.p7c0a+0ldap://smime2.nist.gov/cn=Test%20PIV-I%20RSA%202048-bit%20CA%20for%20Test%20PIV%20Cards,ou=Test%20CA,o=Test%20Certificates%202010,c=US?cACertificate;binary,crossCertificatePair;binary0(+0http://seclab7.ncsl.nist.gov0U0%aU%0+ +7U%0U 00  `He0G0lUe0c2 +7$ "pivitestcardholder@upn.example.com-urn:uuid:048051b4-2288-41fd-b895-5fe9945e1c630  *H  ,!Jm[Lu\ $oqCW;]zȰ3/a#CZcO@,:NdG6jG(yϟkq, ,\Įy C]j@0Ĺ Đ]3 ʼnld.yz菽 s jbc1J&Hs Ε֊BF_5x95"|Xm\.qjjaSp00kB0  *H  0x1 0 UUS10U Test Certificates 201010U Test CA1604U-Test PIV-I RSA 2048-bit CA for Test PIV Cards0 101001083000Z 301001083000Z01 0 UUS10U Test Government10U Test DepaaaSp00kB0  *H  0x1 0 UUS10U Test Certificates 201010U Test CA1604U-Test PIV-I RSA 2048-bit CA for Test PIV Cards0 101001083000Z 301001083000Z01 0 UUS10U Test Government10U Test Depaartment10U  Test Agency1-0+U$048051b4-2288-41fd-b895-5fe9945e1c630"0  *H 0 b~M!z}OsǿdSEHކ[ W52sҌ,k!SaaF@D.<"nPQq? \>6`#YZuv"fD[1 cI;'$oam ?n2ƒdto/rTWFiG)4v2*]42ƨeq |eﷴsԡ2cymVKG[00U#0I.|7(,30U=vYv(:U͕0U00⠁ߠ܆0http://smime2.nist.gov/PIVTest/RSA2048PIVICA.carlldap://smime2.nist.gov/cn=Test%20PIV-I%20RSA%202048-bit%20CA%20for%20Test%20PIV%20Cards,ou=Test%20CA,o=Test%20Certificates%202010,c=US?certificateRevocationList;binary0P+B0>0K+0?http://smime2.nist.gov/PIVTest/CACertsIssuedToRSaA2048PIVICA.p7c0+0ldap://smime2.nist.gov/cn=Test%20PIV-I%20RSA%202048-bit%20CA%20for%20Test%20PIV%20Cards,ou=Test%20CA,o=Test%20Certificates%202010,c=US?cACertificate;binary,crossCertificatePair;binary0(+0http://seclab7.ncsl.nist.gova0U0U% 0 `He0U 00  `He0H08U10/-urn:uuid:048051b4-2288-41fd-b895-5fe9945e1c630  *H  ⼔'I}o%+#b],أ1?ʡTGAHz=tK:0  *H  ć Z@iUoUa[@fVf)3b&|0o Y8stvUyIzG+[3GއcT4ʱ*E ,\ʋD6[^Z4 XF*d ҅rc!gD;FK*,K5<赙Qa+Y/I}&+["[ %sgx I$B]y|e ¶I97(/03Sa~O _/@a~O _/@jOpenSC-0.26.1/src/tests/fuzzing/corpus/fuzz_pkcs15_tool/a1e04ba21b4b54a1fdc048611c4f890405c9885d000066400000000000000000000052001474147347300304060ustar00rootroot00000000000000-k-C-s ;1Xjj*o%P1<> 0?PD 0?PDA 0?PDQ 0?PDq 0?PD*o%P2240*PX Volkswagen AG VW PKI Cardrd*o%Dq!00 ProfileId0 0?P*o%ACVW_RaSiemens53_SLE78CFX3000P_AuthOKG_NonRepOKG_8CA_11KAR_V1_4.cpfoDl/080 Digital Signature0 40 0?P0:0 Non Repudiation0 3@0 0?P0<0 Encryption0ZD00 ?Pv$oD 0F0 Card PIN0,0* 00000000000000Z0?0:0 Signature PIN00L 0?OpenSC-0.26.1/src/tests/fuzzing/corpus/fuzz_pkcs15_tool/a3289103f478310fe6013369adf1b1e0e44b14c2000066400000000000000000000236701474147347300302540ustar00rootroot00000000000000-k-C-s;ߖEs̑yjjjjm ~O a ~O _/@ SaS~O _/@ ~O a ~O _/@ SaS~O _/@~O _/@ S 0PaS 0PYmphXS$B"PC4520301231>0 *H 010  `He0 `He 000  *H  0r1 0 UUS10U Test Certificates 201010U Test CA100.U'Test RSA 2048-bit CA for Taest PIV Cards0 101001083000Z 301001083000Z0e1 0 UUS10U Test Government10U Test Department1"0 UTest PIV Content Signer 10"0  *H 0 "fVt%BĄWQs(eMsB~;PG/׫CӺx?27?҈+Y4au{E )c\ҁ,6=zp +U(hEbdPE7'N_:p 7ģ|@V"9FEk,i[}}wn>MG pHxaFr󟳐al P+S/)­rE7[n듆,S +s00D+6020G+0;http://smime2.nist.gov/PIVaTest/CACertsIssuedToRSA2048CA.p7c0+0ldap://smime2.nist.gov/cn=Test%20RSA%202048-bit%20CA%20for%20Test%20PIV%20Cards,ou=Test%20CA,o=Test%20Certificates%202010,c=US?cACertificate;binary,crossCertificatePair;binary0(+0http://seclab7.ncsal.nist.gov0U00֠ӠІ,http://smime2.nist.gov/PIVTest/RSA2048CA.crlldap://smime2.nist.gov/cn=Test%20RSA%202048-bit%20CA%20for%20Test%20PIV%20Cards,ou=Test%20CA,o=Test%20Certificates%202010,c=US?certificateRevocationList;binary0U?GaWx륔g)s0U#0HDtW 0U 00  `He0U% 0 `He0U0  *H  T>kDq 쯞^^5N%3D9 ^4 1PLpN  ~PwC 3X]z.]/YV5$;&Gz/qLn}w`Ia[T 0vgԋ: & eH w 0٬#P֩^],=r;<m[~ƬjߣUY˾fNӐ"N9e3]s-u10~0w0r1 0 UUS10U Test Certificates 201010U Test CA100.U'Test RSA 2048-bit CA for Test PIV Caards0  `He0 *H  1 `He0 *H  1 110403194843Z0/ *H  1" Fn}ΓB)lW|nӔ0s`He1g0e1 0 UUS10U Test Government10U Test Department1"0 UTest PIV Content Signer 10  *H a qb;Z>CsKVP jBD']S͹JҚ(5܇{4 疳TY*4!~Y| ψڮH/Ts¶%}&j{$g,B[ VY=]r_5˶633uؤ.ɲ]t(SUk#ݲ\r8IvSK꼹j6˩&xih*kbH^GX9zDa +|#ܸ S(paS(p000  *H 0r1 0 UUS10U Test Certificates 201010U Test CA100.U'Test RSA 2048-bit CA for Test PIV Cards0 080301083000Z 110301083000Z0v1 0 UUS10U Test Government10U Test Department1a0U  Test Agency10UTest Cardholder XIII00  *H 0]'#d[fC:%g~ e|U59~tU_b|sMlI7v)G׮P&L9YpV~!sBJ)o±q9050U#0HDtW 0aU,tg?/Y 0U00֠ӠІ,http://smime2.nist.gov/PIVTest/RSA2048CA.crlldap://smime2.nist.gov/cn=Test%20RSA%202048-bit%20CA%20for%20Test%20PIV%20Cards,ou=Test%20CA,o=Test%20Certificates%202010,c=US?certificateRevocationList;bainary0D+6020G+0;http://smime2.nist.gov/PIVTest/CACertsIssuedToRSA2048CA.p7c0+0ldap://smime2.nist.gov/cn=Test%20RSA%202048-bit%20CA%20for%20Test%20PIV%20Cards,ou=Test%20CA,o=Test%20Certificates%202010,c=US?cACertificatea;binary,crossCertificatePair;binary0(+0http://seclab7.ncsl.nist.gov0U0%U%0+ +7U%0U 00  `He 0 `He 0bU[0Y. +7 32018949244215@upn.example.com'`HePYamphXS$B"PC0  *H D:`6a'tڙHo8fPML Sсa͚-~MqPSD`UeIjʉIz]\SEIîEYwȾCcuJ&v#Ll/840"eYzU&Hz ](Z[kDk =|ˣBa0QA|ۙ`a,.g0vMcp.c겴u)N[q S>p5aS>p50100  *H 0r1 0 UUS10U Test Certificates 201010U Test CA100.U'Test RSA 2048-bit CA for Test PIV Cards0 080301083000Z 110301083000Z0v1 0 UUS10U Test Government10U Test Department1a0U  Test Agency10UTest Cardholder XIII0"0  *H 0 VGg/ \B Ȓ 8q[TO7Բ.Ct$G3a\nS5y {^uYSMHA31dWҭTk'# I: \ً7G,'̨;m0J2pax#(PL8a-̒ \ -m7L:r"0d̃˹"Gܯ\'F&z9OW2J5lXQ/|kVQ00U#0HDtW 0UD=ieϚkjgts0U00֠ӠІ,http://smime2.nist.gov/PIVTest/RSA2048CA.crlldap://smime2.nist.gova/cn=Test%20RSA%202048-bit%20CA%20for%20Test%20PIV%20Cards,ou=Test%20CA,o=Test%20Certificates%202010,c=US?certificateRevocationList;binary0D+6020G+0;http://smime2.nist.gov/PIVTest/CACertsIssuedToRSA2048CA.p7c0+0ldap://samime2.nist.gov/cn=Test%20RSA%202048-bit%20CA%20for%20Test%20PIV%20Cards,ou=Test%20CA,o=Test%20Certificates%202010,c=US?cACertificate;binary,crossCertificatePair;binary0(+0http://seclab7.ncsl.nist.gov0U0U 00  `He0-Ua&0$"test.cardholder13@mail.example.com0  *H {e克tA$}qN=1W䲏$ =Pεkqvn X-~O 5- <ARA#LG*I#= CsJy J}ĠP3 @# i=қUzDV 4 XXΘWL$aBD +aJ1(/xu0!%TB:)1revY- m q S>p5aS>p50100  *H 0r1 0 UUS10U Test Certificates 201010U Test CA100.U'Test RSA 2048-bit CA for Test PIV Cards0 080301083000Z 110301083000Z0v1 0 UUS10U Test Government10U Test Department1a0U  Test Agency10UTest Cardholder XIII0"0  *H 0 >9pŋIUiuɗp !o 4s k>GX o&q5GU|׺/…sӼwL&ZVr?@8T)Gv4M$$NppgBkDL_%U#ctYQ2_T ^ZXkpR{Aa|;M胄PyJG,HK Ƽ&SXE `"5SfYNL 3kW$i00U#0HDtW 0UplVٴ2ڳ4!0U00֠ӠІ,http://smime2.nist.gov/PIVTest/RSA2048CA.crlldap://smime2.nist.gova/cn=Test%20RSA%202048-bit%20CA%20for%20Test%20PIV%20Cards,ou=Test%20CA,o=Test%20Certificates%202010,c=US?certificateRevocationList;binary0D+6020G+0;http://smime2.nist.gov/PIVTest/CACertsIssuedToRSA2048CA.p7c0+0ldap://samime2.nist.gov/cn=Test%20RSA%202048-bit%20CA%20for%20Test%20PIV%20Cards,ou=Test%20CA,o=Test%20Certificates%202010,c=US?cACertificate;binary,crossCertificatePair;binary0(+0http://seclab7.ncsl.nist.gov0U 0U 00  `He0-Ua&0$"test.cardholder13@mail.example.com0  *H Ԇ-Bd] ݺVC%@'sQMMsm۸n$#tE[9#yB&ҋ`߂nK%g(OB튊ڏFbw|()ڲcph1 ᤌ 0@6J:+PwjhYO->++3%#$`o4T{Λn3snƮ=~mOڮ zRN_>k&CU#00aU#0HDtW 0U,5SSz`0U00֠ӠІ,http://smime2.nist.gov/PIVTest/RSA2048CA.crlldap://smime2.nist.gov/cn=Test%20RSA%202048-bit%20CA%20for%20Test%20PIV%20Cards,ou=Test%20CA,o=Test%20Certificates%202010,ca=US?certificateRevocationList;binary0D+6020G+0;http://smime2.nist.gov/PIVTest/CACertsIssuedToRSA2048CA.p7c0+0ldap://smime2.nist.gov/cn=Test%20RSA%202048-bit%20CA%20for%20Test%20PIV%20Cards,ou=Test%20CA,o=Test%20Certifiacates%202010,c=US?cACertificate;binary,crossCertificatePair;binary0(+0http://seclab7.ncsl.nist.gov0U0U% 0 `He0U 00  `He0 `He 02U+0)'`HePYmphXS$B"PC0  *H aW;Ɋh?9T$Hbȓf/aLw-:/V[ d BV=wmp̈]AEE1s^GIꐉTiS>*pb#0:mEy^= IEcLG1v lR1hHGz ߕ]V ,[ɸ+xXMd0V'0%  ?Ӱq{J-V?p1 00w0r1 0 UUS10U aTest Certificates 201010U Test CA100.U'Test RSA 2048-bit CA for Test PIV Cards0  `Hee0 *H  1+0 *H  1 110403195242Z0/ *H  1" ڽ viftGRVHc1+_$@]y0  *H  `P?xו!XpE+ y9D]a'if8\8tI^w4MBٟsxʻif=/lD8"k>we|Jo^YG xLcR(@%!IeA[ŷ}6';S ;9U6R! XNDZ{WKU E}!3pv(Г*fRB&_(C9RR%}uHMsr\uλ fsXoy ѵ吐Ld87ZMnϜOwJ-/Вn pyPŴ˫M wɈv "-fmy%G|D۔\rF??En=ºT/<_D%s6,'MmzƷ$YX[~:1AbmϩLq,vѴlo+6-fbfd`\ĤcĤa TY>16?}SæȃY$ DTk pp1?}j{xbPR RaPfPhAA[^FII~rjQIJbI^4V! O.)/%"ԲJX2xb5hR4=nƝll쌌,k WD 2IrNy25ť_q PZ6`k <~F,,L 6l@XEY tTa\Z 9&nݫL)LR TYr3 ELl\PD A*Y 4!I0/ d$?wg= ϐHR`(t^m\q}P '(g;.%}ӥi~laţOsO딣v߷7^OFUiO'h,2yuk'߳jjÅ6o)9W_:/.nI~FW=)ɶS,xp՟sI-wL=[K0TVmuڭy;Of$9aم;MOc]>gCff^o^s1=eBsBʳNئ_+-?KCY^UY}|-ԹR^tV/oj 1OaoECCx3hb5hb]'WI]8p0ejh`8ߐۀ9M)4P@/*(/Pp/*MSp/*/J,340)VP晗_ V^ihn` ,Tt nd 'khahddbhilhbZ"q TW~qFi" 3s #M&F%cde`nbgs151229|Tx1̐UKԧ^>[ s(4hv/~Qp8ǦVZ?oE7Iq^+>Mb|"G:ʩf78.Lޝ޿u_Z/ށ)O_TgvR`͍oD0⎞T Uc[^/S~POԺq^gE]ʙ{m[|$oioZvDLdI-?\[9A;GĽ  y dA*""d۾ط7*xs A,b "G8>5½wd1)g0(3(YP-/J_?9$%$Q/(KυK'~PjY~vjS%,@77CTz)&LPy]@eϺ{?<\ߣxg ;} 1J#n^Y]g"5vNuKK9qWlUwO(yYg)+or”~f/|^V8B7ߞ:?Q)3dS`Z7;?cDc<-u"mfbfd`\ĤmĤn TY>1U+:خӶK@$"a Z[poS/۞pKzy 2E 2JJ SJRK򠱚T\~rQN~1(g8U’48A8u4dfcdeegddi\cиҠ&bԖIs ̓,..%蘠K 2@O..^>P32gaafbo0fd.΢gær4(bxU._1ynXĘ(e2LEg w015IRE@c (H>rsws  D+YE'^)gwo٩6ӚXϯajߟ*rIm(tzL\-/ul_:;fsX>Ś٪wq srgtd|Wby?𸠞ݝi.ۢci. _n0Fo'B'ImȼLU!Gb0l ;q.,Um;jsgfb۩/$s!UMnս*ߋ<?6}VwTaoQ CC++x3hbW7hb_'WI]8p6ejh`8ߐۀ9M)4P@/*(/Pp/*MSp/*/J,340)VP晗_ V^ihn` ,Tt nd 'khahddbhilddZ"q TW~qFi" 3s #M&F%cde`nbgs15122\ $Wv@VFUV;\]Uʒ6#sZEξqܱzq6;ܓlVd?v^/)7|VM!z! Zq˟ zU;,Ѻ{Rxu6uV9?s'7K;_~|]G7hDzVYo{4kλςU}q`Hܩ_+8V(_,`L\آ A9^MĎn f0&21320.nbV2hb3,m;OJ[~J A,b "G8>5½wd1)g0(3(YP-/J_?9$%$Q/(KυK'~PjY~vjS%,@h^z28PK^`96}5~EWnvw9[vZ[O wfS_&}/X6eӣyof4cwc$\}>:Ò)|Eua g-w.tٷ?!^/sUNX7k]_n:ĿC^9W_H1;ٽv S9a{G,1|S +~*HN/MC(񳹖a~GU٫6থ?O{~)9yCV'5O-"de yfvQx%G)5Ң;N<ĵڛ˓%.S`u,ek~㟎Αwl8h&{]oõ>}g|[j|*U뛯,'["*FEgQF+oao CCx3hb;ojOZv7q gƩq!'s( 0Sh2%,_TQ_^T_T_Xgh`R-3/-(L!$59#/?'?YX %@23=/3/]Q@N ȵDD2faD=EyE .fFMJ!bjbddv7n F;wӛIIl-o/~exݟ7$͝h2(.#gqIZWDo˜aͮ=m{ s^? q,3N|a>Y!ƘŞʔ\5[wMʐŴɮЌή׮mcvQ[kY8YhejRvμXh~ br.,]mLץ׿OOE|b>H]U_m4Н/> 9yA uVL~&>^ռןo5-Xs9Iܟg;63\ݠ17 x6kebd62(CZ@MUr^AJ],4IA *@ab^ #)iܝKK22K*yD-EP#ò׽n-g=4ΊO/z*jS٩gؐ7 2XU)H>/9hpf/,rnNŠ;jdsOrS}աon3sSM% 6]^*p3`68Z]o.9ƕU$V_{{Q=5Oq>ɮ5Eglm]XU1ݏ_Yo?黲_pgzjk}I'ge!OpenSC-0.26.1/src/tests/fuzzing/corpus/fuzz_pkcs15_tool/f1cac4a34dcb285f87df7a4568fe8eb00f9a4cad000066400000000000000000000033671474147347300311670ustar00rootroot00000000000000--change-pin--pin111111--new-pin123456;1s!WYubiKey@jjjmmmaOyO ~O a ~O _/@ ~O a ~O _/@~O _/@j~O _/@ S;09a5?S;09s9shX!B!B4phV2Hl520300101>j S*p!aS*p!00ĠZ\K‹^ q;0 *H=01 0 U user0 220410173930Z 240201000000Z01 0 U user0Y0*H=*H=BG>p}kj}7 o̺HE2ӴA|^&2n?%@0 *H=H0E!/)N5(llV?gOa.0  7̭U QXw #su7gq Sfp]aSfp]0Y0VɃ:;z[o?œ|0 *H=01 0 U user0 220410174126Z 240420000000Z01 0 U user0v0*H=+"bTfll <3SdV/l80RgN^ŷzbCn# e6e̫,lVj5aVӏ\b50 *H=g0ajld0.1"vpÓJ<}rߚ6ix2mb'O0eqq–da|~A{V!rj>=cq SpaSp00z Entg$xn0  *H  01 0 U user0 220418192333Z 230630000000Z01 0 U user0"0  *H 0 @,W/<`ĸ10Lm23VH'a"+-z|Ov܏h<L4ʁ#e,*7-,RCRUx2xa ; E|^Bi\?1nM|Ez_xţД}7WEE;<88~C`|f>f[mřZ\e|>Svp% 9RϦ mGˤi\c(1on'o{bqI0  *H  t aKd¾NJ 2R3y[do1 I;;\BiO? x7aа=ww&D/#2=3\1mp\z!&V:v~K.\/~)Fב!.J7ڝ)i>V# +6,~SѶ@p] C>i mv`a *ݍ`wa(cSt\uO&||q~O _/@OpenSC-0.26.1/src/tests/fuzzing/corpus/fuzz_pkcs15init/000077500000000000000000000000001474147347300227555ustar00rootroot00000000000000OpenSC-0.26.1/src/tests/fuzzing/corpus/fuzz_pkcs15init/6ce966ee0f311e1a63f2bb693caeba1b0fd1160e000066400000000000000000001150511474147347300306160ustar00rootroot00000000000000# # PKCS15 profile, generic information. # This profile is loaded before any card specific profile. # cardinfo { label = "MyEID"; manufacturer = "Aventra Ltd."; min-pin-length = 4; max-pin-length = 8; pin-encoding = ascii-numeric; pin-pad-char = 0xFF; } # # 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 = no; # 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 # rfc2459: SHA1(SequenceASN1 of public key components as ASN1 integers) # default value: 'native' pkcs15-id-style = mozilla; } # Default settings. # This option block will always be processed. option default { macros { protected = *=$SOPIN, READ=NONE; unprotected = *=NONE; so-pin-flags = local, initialized, needs-padding, soPin; so-min-pin-length = 6; so-pin-attempts = 2; so-auth-id = FF; so-puk-attempts = 4; so-min-puk-length = 6; unusedspace-size = 510; odf-size = 255; aodf-size = 255; cdf-size = 1530; cdf-trusted-size = 510; prkdf-size = 1530; pukdf-size = 1530; skdf-size = 1530; dodf-size = 1530; } } # This option sets up the card so that a single # user PIN protects all files option onepin { macros { protected = *=$PIN, READ=NONE; unprotected = *=NONE; so-pin-flags = local, initialized, needs-padding; so-min-pin-length = 4; so-pin-attempts = 3; so-auth-id = 1; so-puk-attempts = 7; so-min-puk-length = 4; } } # This option is for cards with very little memory. # It sets the size of various PKCS15 directory files # to 128 or 256, respectively. option small { macros { odf-size = 128; aodf-size = 128; cdf-size = 256; prkdf-size = 128; pukdf-size = 128; dodf-size = 128; } } # This option tells pkcs15-init to use the direct option # when storing certificates on the card (i.e. put the # certificates into the CDF itself, rather than a # separate file) option direct-cert { pkcs15 { direct-certificates = yes; encode-df-length = yes; } macros { cdf-size = 3192; } } # 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 { reference = 1; min-length = 4; max-length = 8; attempts = 3; flags = initialized, needs-padding; } PIN user-puk { min-length = 4; max-length = 8; attempts = 10; flags = needs-padding; } PIN so-pin { reference = 3; auth-id = FF; min-length = 4; max-length = 8; attempts = 3; flags = initialized, soPin, needs-padding; } PIN so-puk { min-length = 4; max-length = 8; attempts = 10; flags = needs-padding; } filesystem { DF MF { path = 3F00; type = DF; acl = CREATE=$PIN, DELETE=$SOPIN; # This is the DIR file EF DIR { type = EF; file-id = 2F00; structure = transparent; size = 128; acl = READ=NONE, UPDATE=$SOPIN, DELETE=$SOPIN; } # 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 = DELETE=$PIN, CREATE=$PIN; size = 5000; EF PKCS15-ODF { file-id = 5031; size = $odf-size; structure = transparent; acl = READ=NONE, UPDATE=$PIN, DELETE=$SOPIN; } EF PKCS15-TokenInfo { file-id = 5032; size = 160; structure = transparent; acl = READ=NONE, UPDATE=$SOPIN, DELETE=$SOPIN; } EF PKCS15-UnusedSpace { file-id = 5033; structure = transparent; size = $unusedspace-size; acl = READ=NONE, UPDATE=$SOPIN, DELETE=$SOPIN; } EF PKCS15-AODF { file-id = 4401; structure = transparent; size = $aodf-size; acl = READ=NONE, UPDATE=$SOPIN, DELETE=$SOPIN; } EF PKCS15-PrKDF { file-id = 4402; structure = transparent; size = $prkdf-size; acl = *=NEVER, READ=NONE, UPDATE=$PIN, DELETE=$SOPIN; } EF PKCS15-PuKDF { file-id = 4404; structure = transparent; size = $pukdf-size; acl = *=NEVER, READ=NONE, UPDATE=$PIN, DELETE=$SOPIN; } EF PKCS15-SKDF { file-id = 4407; structure = transparent; size = $skdf-size; acl = *=NEVER, READ=NONE, UPDATE=$PIN, DELETE=$SOPIN; } EF PKCS15-CDF { file-id = 4403; structure = transparent; size = $cdf-size; acl = *=NEVER, READ=NONE, UPDATE=$PIN, DELETE=$SOPIN; } EF PKCS15-CDF-TRUSTED { file-id = 4405; structure = transparent; size = $cdf-trusted-size; acl = *=NEVER, READ=NONE, UPDATE=$PIN, DELETE=$SOPIN; } EF PKCS15-DODF { file-id = 4406; structure = transparent; size = $dodf-size; acl = *=NEVER, READ=NONE, UPDATE=$PIN, DELETE=$SOPIN; } EF template-private-key { type = internal-ef; file-id = 4B01; acl = CRYPTO=$PIN, UPDATE=$PIN, DELETE=$PIN, GENERATE=$PIN; } EF template-secret-key { type = internal-ef; file-id = 4D01; acl = CRYPTO=$PIN, UPDATE=$PIN, DELETE=$PIN, GENERATE=$PIN; } EF template-public-key { structure = transparent; file-id = 5501; acl = READ=NONE, UPDATE=$PIN, DELETE=$PIN, GENERATE=$PIN; } EF template-certificate { file-id = 4301; structure = transparent; acl = READ=NONE, UPDATE=$PIN, DELETE=$PIN; } template key-domain { # This is a dummy entry - pkcs15-init insists that # this is present EF private-key { file-id = 4B01; type = internal-ef; acl = CRYPTO=$PIN, UPDATE=$PIN, DELETE=$PIN, GENERATE=$PIN; } EF public-key { file-id = 5501; structure = transparent; acl = READ=NONE, UPDATE=$PIN, DELETE=$PIN, GENERATE=$PIN; } EF secret-key { file-id = 4D01; type = internal-ef; acl = CRYPTO=$PIN, UPDATE=$PIN, DELETE=$PIN, GENERATE=$PIN; } # Certificate template EF certificate { file-id = 4301; structure = transparent; acl = READ=NONE, UPDATE=$PIN, DELETE=$PIN; } EF privdata { file-id = 4501; structure = transparent; acl = READ=$PIN, UPDATE=$PIN, DELETE=$PIN; } EF data { file-id = 4601; structure = transparent; acl = READ=NONE, UPDATE=$PIN, DELETE=$PIN; } } } } } ;1EMyEID)o%8P cPKCS-15MyEIDU0VX  jMyEIDU0VXo8??j)o%8P cPKCS-15j)o%8P cPKCS-15oD?j)o%8P cPKCS-15oP1?j)o%8P cPKCS-15oD?oP1?j)o%8P cPKCS-15oD?oP1?j)o%8P cPKCS-15oD?oP1?j)o%8P cPKCS-15oD?oP1?j)o%8P cPKCS-15oD?oP1?MyEIDU0VX+j)o%8P cPKCS-15oD?oP1?jjo8??o/?o/?o/?j)o%8P cPKCS-15oP2?j)o%8P cPKCS-15oIF?j)o%8P cPKCS-15oP1? 0?PD 0?PD 0?PD 0?PD 0?PD 0?PD 0?PDoP2?0 U0VX Aventra Ltd.MyEID0t0  `He0  `He0  `He)0  `He*20211220114021ZoD?0;0 Security Officer PIN00 oD?oD?jj)o%8P cPKCS-15oFoD?oD?oD?oD?oD?jj)o%8P cPKCS-15oK2mH\Lj>]m mFo5BX/7Zpn[0 1Ys | d"1^&>yUjZ;rf:<4oI?g ܩ2QjoK2mH\Lj>]m mFo5BX/7Zpn[0 1Ys | d"1^&>yUjZ;rf:<4oI?g ܩ2QoD?jj)o%8P cPKCS-15oUoD?oD?0K0 Private Key0!=EӐ2&R$H< v|y 00?PKoD?0G0 Private Key@0 =EӐ2&R$H< v|y00?PUoD?oD?joKjj)o%8P cPKCS-15o"KEA`p \v8Knc?%t_ ӶkH=c5SOа4"pQmeɐjo"KEA`p \v8Knc?%t_ ӶkH=c5SOа4"pQmeɐoD?jj)o%8P cPKCS-15o[UoD?oD?oD?oD?oD?jjoD?oD?oD?oD?jjoD?oD?oD?oD?jjj)o%8P cPKCS-15oP1? 0?PD 0?PD 0?PD 0?PD 0?PD 0?PD 0?PDoP2?0 U0VX Aventra Ltd.MyEID0t0  `He0  `He0  `He)0  `He*20211013154624ZoD?0;0 Security Officer PIN00 000 Basic PIN00 o8??OpenSC-0.26.1/src/tests/fuzzing/corpus/fuzz_scconf_parse_string/000077500000000000000000000000001474147347300250165ustar00rootroot00000000000000497025125e0dfab0b9e16155ce16d6e25ec8ec6d000066400000000000000000000121721474147347300324020ustar00rootroot00000000000000OpenSC-0.26.1/src/tests/fuzzing/corpus/fuzz_scconf_parse_stringapp 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.tokend on Mac OS X only app tokend { framework tokend { score = 10; ignore_private_certificate = false; } } # Used by OpenSC minidriver on Windows only app cardmod { } fa7e8cb717af33932718d96a3c785268311d9c6f000066400000000000000000000002421474147347300322100ustar00rootroot00000000000000OpenSC-0.26.1/src/tests/fuzzing/corpus/fuzz_scconf_parse_stringapp default { debug = 0; debug_file = stdout; framework pkcs15 { enable_builtin_emulation = yes; builtin_emulators = old, jpki, dnie, gids, PIV-II; } } OpenSC-0.26.1/src/tests/fuzzing/fuzz_asn1_print.c000066400000000000000000000020361474147347300216730ustar00rootroot00000000000000/* * 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" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { #ifdef FUZZING_ENABLED fclose(stdout); #endif sc_asn1_print_tags(Data, Size); return 0; } OpenSC-0.26.1/src/tests/fuzzing/fuzz_asn1_sig_value.c000066400000000000000000000033221474147347300225140ustar00rootroot00000000000000/* * 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; } OpenSC-0.26.1/src/tests/fuzzing/fuzz_card.c000066400000000000000000000054431474147347300205330ustar00rootroot00000000000000/* * 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; } OpenSC-0.26.1/src/tests/fuzzing/fuzz_piv_tool.c000066400000000000000000000077571474147347300214670ustar00rootroot00000000000000/* * 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; evpkey = 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; } OpenSC-0.26.1/src/tests/fuzzing/fuzz_pkcs11.c000066400000000000000000000762071474147347300207320ustar00rootroot00000000000000/* * 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; int rv = CKR_OK, 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, 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 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); int to_process = 0, 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 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; } OpenSC-0.26.1/src/tests/fuzzing/fuzz_pkcs15_crypt.c000066400000000000000000000103061474147347300221430ustar00rootroot00000000000000/* * 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; } OpenSC-0.26.1/src/tests/fuzzing/fuzz_pkcs15_decode.c000066400000000000000000000066401474147347300222330ustar00rootroot00000000000000/* * 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; } OpenSC-0.26.1/src/tests/fuzzing/fuzz_pkcs15_encode.c000066400000000000000000000051471474147347300222460ustar00rootroot00000000000000/* * 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; } OpenSC-0.26.1/src/tests/fuzzing/fuzz_pkcs15_reader.c000066400000000000000000000130061474147347300222440ustar00rootroot00000000000000/* * 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; } OpenSC-0.26.1/src/tests/fuzzing/fuzz_pkcs15_tool.c000066400000000000000000000046061474147347300217650ustar00rootroot00000000000000/* * 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; } OpenSC-0.26.1/src/tests/fuzzing/fuzz_pkcs15init.c000066400000000000000000000256731474147347300216230ustar00rootroot00000000000000/* * 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); if (algorithms[i] == SC_ALGORITHM_EC) free(keygen_args.prkey_args.key.u.ec.params.named_curve); } } 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 = (keybits[i] + 7) / 8; 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; } OpenSC-0.26.1/src/tests/fuzzing/fuzz_scconf_parse_string.c000066400000000000000000000026451474147347300236560ustar00rootroot00000000000000/* * 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; } OpenSC-0.26.1/src/tests/fuzzing/fuzzer.c000066400000000000000000000035421474147347300200670ustar00rootroot00000000000000/* * 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; } OpenSC-0.26.1/src/tests/fuzzing/fuzzer_reader.c000066400000000000000000000130131474147347300214030ustar00rootroot00000000000000/* * 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; } OpenSC-0.26.1/src/tests/fuzzing/fuzzer_reader.h000066400000000000000000000024741474147347300214210ustar00rootroot00000000000000/* * 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 */ OpenSC-0.26.1/src/tests/fuzzing/fuzzer_tool.c000066400000000000000000000075711474147347300211320ustar00rootroot00000000000000/* * 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); } OpenSC-0.26.1/src/tests/fuzzing/fuzzer_tool.h000066400000000000000000000027661474147347300211400ustar00rootroot00000000000000/* * 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 */ OpenSC-0.26.1/src/tests/lottery.c000066400000000000000000000033361474147347300165510ustar00rootroot00000000000000/* Copyright (C) 2001 Juha Yrjölä * All rights reserved. */ #include "config.h" #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_SYS_TIME_H #include #endif #include "libopensc/opensc.h" #include "sc-test.h" int main(int argc, char *argv[]) { int i, c, r, cnt = 3, freq[39]; struct timeval tv1, tv2; u8 buf[14]; sc_test_init(&argc, argv); for (i = 0; i < 39; i++) freq[i] = 0; c = 0; while (cnt) { u8 nbuf[39]; for (i = 0; i < 39; i++) { nbuf[i] = i + 1; } if (c == 0) { if (0 != gettimeofday(&tv1, NULL)) { fprintf(stderr, "gettimeofday() failed: %s\n", strerror(errno)); sc_test_cleanup(); return 1; } } sc_lock(card); r = sc_get_challenge(card, buf, 14); sc_unlock(card); if (r == 0) { int left = 39; printf("Lottery: "); for (i = 0; i < 7; i++) { unsigned short s = buf[2 * i] + (buf[2 * i + 1] << 8); int lot = s % left; int num = nbuf[lot]; nbuf[lot] = nbuf[left - 1]; left--; freq[num - 1]++; printf("%3d ", num); } printf("\n"); } else { fprintf(stderr, "sc_get_challenge() failed: %s\n", sc_strerror(r)); sc_test_cleanup(); return 1; } c++; if (c == 50) { unsigned long long foo, foo2; gettimeofday(&tv2, NULL); foo = tv2.tv_sec * 1000 + tv2.tv_usec / 1000; foo2 = tv1.tv_sec * 1000 + tv1.tv_usec / 1000; printf("Time per one: %lld ms\n", (foo - foo2) / 50); printf("Frequencies:\n"); for (i = 0; i < 39; i++) { printf("%3d: %-5d", i + 1, freq[i]); if (((i + 1) % 10) == 0) printf("\n"); } printf("\n"); c = 0; cnt--; } } sc_test_cleanup(); return 0; } OpenSC-0.26.1/src/tests/opensc-minidriver-test.c000066400000000000000000000741341474147347300214650ustar00rootroot00000000000000/** * 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); } OpenSC-0.26.1/src/tests/p11test/000077500000000000000000000000001474147347300161775ustar00rootroot00000000000000OpenSC-0.26.1/src/tests/p11test/Makefile.am000066400000000000000000000022051474147347300202320ustar00rootroot00000000000000include $(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 OpenSC-0.26.1/src/tests/p11test/Makefile.mak000066400000000000000000000011301474147347300204010ustar00rootroot00000000000000TOPDIR = ..\..\.. 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) /pdb:$*.pdb /out:$@ $*.obj $(OBJECTS) $(LIBS) if EXIST $@.manifest mt -manifest $@.manifest -outputresource:$@;1 OpenSC-0.26.1/src/tests/p11test/README.md000066400000000000000000000030641474147347300174610ustar00rootroot00000000000000# 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 OpenSC-0.26.1/src/tests/p11test/cert.cfg000066400000000000000000000001451474147347300176150ustar00rootroot00000000000000organization = "OpenSC" expiration_days = 365 email = "none@example.org" signing_key encryption_key OpenSC-0.26.1/src/tests/p11test/epass2003_ref.json000066400000000000000000000413021474147347300213460ustar00rootroot00000000000000{ "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" }] } OpenSC-0.26.1/src/tests/p11test/isoapplet_ref_v0.json000066400000000000000000000072511474147347300223400ustar00rootroot00000000000000{ "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", "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" }] } OpenSC-0.26.1/src/tests/p11test/isoapplet_ref_v1.json000066400000000000000000000144061474147347300223410ustar00rootroot00000000000000{ "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", "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" }] } OpenSC-0.26.1/src/tests/p11test/openpgp_s0_ref.json000066400000000000000000000064201474147347300220020ustar00rootroot00000000000000{ "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", "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" }] } OpenSC-0.26.1/src/tests/p11test/openpgp_s1_ref.json000066400000000000000000000061431474147347300220050ustar00rootroot00000000000000{ "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", "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" }] } OpenSC-0.26.1/src/tests/p11test/oseid_ref.json000066400000000000000000000450301474147347300210330ustar00rootroot00000000000000{ "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", "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" }] } OpenSC-0.26.1/src/tests/p11test/p11test.c000066400000000000000000000114331474147347300176460ustar00rootroot00000000000000/* * 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); } OpenSC-0.26.1/src/tests/p11test/p11test.supp000066400000000000000000000011221474147347300204050ustar00rootroot00000000000000{ 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 } OpenSC-0.26.1/src/tests/p11test/p11test_case_common.c000066400000000000000000001216551474147347300222210ustar00rootroot00000000000000/* * 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->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; if (o->type == 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; } } else if (o->type == 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; } #ifdef EVP_PKEY_ED25519 } else if (o->type == EVP_PKEY_ED25519) { 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; } #endif #ifdef EVP_PKEY_X25519 } else if (o->type == EVP_PKEY_X25519) { 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; } #endif /* Nothing in the above enum can be used for secret keys */ } else if (o->key_type == 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; } } } /** * 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; 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; } if (EVP_PKEY_base_id(evp) == EVP_PKEY_RSA) { o->key = evp; o->type = EVP_PKEY_RSA; o->bits = EVP_PKEY_bits(evp); } else if (EVP_PKEY_base_id(evp) == EVP_PKEY_EC) { o->key = evp; o->type = EVP_PKEY_EC; o->bits = EVP_PKEY_bits(evp); } else { EVP_PKEY_free(evp); fprintf(stderr, "[WARN %s ]evp->type = 0x%.4X (not RSA, EC)\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; 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; /* 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; 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 *)curve->data, "edwards25519")) { debug_print(" [WARN %s ] Unknown curve name. " " expected edwards25519, got %s", o->id_str, curve->data); } evp_type = EVP_PKEY_ED25519; break; #endif #ifdef EVP_PKEY_X25519 case CKK_EC_MONTGOMERY: if (strcmp((char *)curve->data, "curve25519")) { debug_print(" [WARN %s ] Unknown curve name. " " expected curve25519, got %s", o->id_str, curve->data); } evp_type = EVP_PKEY_X25519; 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) { debug_print(" [WARN %s ] Unknown OID. " " expected NID_ED25519 (%d), got %d", o->id_str, NID_ED25519, nid); } evp_type = EVP_PKEY_ED25519; break; #endif #ifdef EVP_PKEY_X25519 case CKK_EC_MONTGOMERY: if (nid != NID_X25519) { debug_print(" [WARN %s ] Unknown OID. " " expected NID_X25519 (%d), got %d", o->id_str, NID_X25519, nid); } evp_type = EVP_PKEY_X25519; 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 */ a = template[7].pValue; os = d2i_ASN1_OCTET_STRING(NULL, &a, (long)template[7].ulValueLen); if (!os) { debug_print(" [WARN %s ] Cannot decode EC_POINT", o->id_str); return -1; } if (os->length != 32) { debug_print(" [WARN %s ] Invalid length of EC_POINT value", o->id_str); return -1; } key = EVP_PKEY_new_raw_public_key(evp_type, NULL, (const uint8_t *)os->data, os->length); 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)os->length || memcmp(pub, os->data, 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 = 255; } ASN1_STRING_free(os); } 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[], CK_LONG filter_size, CK_ATTRIBUTE template[], CK_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 }; 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 }; 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"; 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"; 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"; 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_EC_COMPRESS; 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); } OpenSC-0.26.1/src/tests/p11test/p11test_case_common.h000066400000000000000000000102731474147347300222170ustar00rootroot00000000000000/* * 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 #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; char *label; CK_ULONG bits; char *value; 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[], CK_LONG filter_size, CK_ATTRIBUTE template[], CK_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 */ OpenSC-0.26.1/src/tests/p11test/p11test_case_ec_derive.c000066400000000000000000000322561474147347300226540ustar00rootroot00000000000000/* * 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" 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 = (o->bits + 7) / 8; 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_x25519(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 = 32, 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; } if (o->type != EVP_PKEY_X25519) { debug_print(" [ KEY %s ] Skip non-EC key for derive", o->id_str); return 1; } /* First, we need to generate our key */ pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_X25519, 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_x25519(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 ? " EC " : o->key_type == CKK_EC_MONTGOMERY ? "EC_M" : " ?? "), 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); } OpenSC-0.26.1/src/tests/p11test/p11test_case_ec_derive.h000066400000000000000000000016471474147347300226610ustar00rootroot00000000000000/* * 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); OpenSC-0.26.1/src/tests/p11test/p11test_case_ec_sign.c000066400000000000000000000055061474147347300223340ustar00rootroot00000000000000/* * 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" 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 = (objects.data[i].bits + 7) / 8; 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); } OpenSC-0.26.1/src/tests/p11test/p11test_case_ec_sign.h000066400000000000000000000016601474147347300223360ustar00rootroot00000000000000/* * 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); OpenSC-0.26.1/src/tests/p11test/p11test_case_interface.c000066400000000000000000000127151474147347300226650ustar00rootroot00000000000000/* * 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(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(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(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((unsigned char *)"PKCS 11", &version, &interface, 0); assert_int_equal(rv, CKR_OK); assert_string_equal(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((unsigned char *)"PKCS 11", &version, &interface, 0); assert_int_equal(rv, CKR_OK); assert_string_equal(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((unsigned char *)"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((unsigned char *)"PKCS 11", &version, &interface, 0); assert_int_equal(rv, CKR_ARGUMENTS_BAD); /* GetInterface with unknown flags */ rv = C_GetInterface((unsigned char *)"PKCS 11", NULL, &interface, 2); assert_int_equal(rv, CKR_ARGUMENTS_BAD); free(interfaces); P11TEST_PASS(info); } OpenSC-0.26.1/src/tests/p11test/p11test_case_interface.h000066400000000000000000000015711474147347300226700ustar00rootroot00000000000000/* * 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); OpenSC-0.26.1/src/tests/p11test/p11test_case_mechs.c000066400000000000000000000201511474147347300220150ustar00rootroot00000000000000/* * 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 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][%4lu] [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); } OpenSC-0.26.1/src/tests/p11test/p11test_case_mechs.h000066400000000000000000000016111474147347300220220ustar00rootroot00000000000000/* * 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); OpenSC-0.26.1/src/tests/p11test/p11test_case_multipart.c000066400000000000000000000076171474147347300227530ustar00rootroot00000000000000/* * 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); } OpenSC-0.26.1/src/tests/p11test/p11test_case_multipart.h000066400000000000000000000016541474147347300227530ustar00rootroot00000000000000/* * 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); OpenSC-0.26.1/src/tests/p11test/p11test_case_pss_oaep.c000066400000000000000000000625311474147347300225370ustar00rootroot00000000000000/* * 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)((o->bits+7)/8 - 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 (bits + 7)/8 - 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); } OpenSC-0.26.1/src/tests/p11test/p11test_case_pss_oaep.h000066400000000000000000000015611474147347300225400ustar00rootroot00000000000000/* * 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); OpenSC-0.26.1/src/tests/p11test/p11test_case_readonly.c000066400000000000000000000636471474147347300225540ustar00rootroot00000000000000/* * 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 #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 = (o->bits+7)/8; 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; if (o->type == 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; } else if (o->type == 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; } #ifdef EVP_PKEY_ED25519 } else if (o->type == EVP_PKEY_ED25519) { /* 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; } #endif } else { 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, NULL_PTR, 0 }; 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; 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; } if (o->type != EVP_PKEY_EC && o->type != EVP_PKEY_RSA #ifdef EVP_PKEY_ED25519 && o->type != EVP_PKEY_ED25519 #endif ) { 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 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_EC_MONTGOMERY) 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 ? "EC_E" : o->key_type == CKK_EC_MONTGOMERY ? "EC_M" : " ?? "), 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; } 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(" [ %-20s ] [ %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); } OpenSC-0.26.1/src/tests/p11test/p11test_case_readonly.h000066400000000000000000000035741474147347300225520ustar00rootroot00000000000000/* * 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); OpenSC-0.26.1/src/tests/p11test/p11test_case_secret.c000066400000000000000000000245731474147347300222170ustar00rootroot00000000000000/* * 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); } OpenSC-0.26.1/src/tests/p11test/p11test_case_secret.h000066400000000000000000000016561474147347300222210ustar00rootroot00000000000000/* * 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); OpenSC-0.26.1/src/tests/p11test/p11test_case_usage.c000066400000000000000000000123261474147347300220270ustar00rootroot00000000000000/* * 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].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].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] [ DERIVE ] [ALWAYS_AUTH]\n"); P11TEST_DATA_ROW(info, 14, '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', "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 ]\n", (o->key_type == CKK_RSA ? "RSA " : o->key_type == CKK_EC ? " EC " : o->key_type == CKK_EC_EDWARDS ? "EC_E" : o->key_type == CKK_EC_MONTGOMERY ? "EC_M" : o->key_type == CKK_AES ? "AES " : o->key_type == CKK_GENERIC_SECRET ? "GEN " : " ?? "), o->bits, o->verify_public == 1 ? " ./ " : " ", o->sign ? "[./] " : "[ ] ", o->verify ? " [./] " : " [ ] ", o->encrypt ? "[./] " : "[ ] ", o->decrypt ? " [./] " : " [ ] ", o->wrap ? "[./]" : "[ ]", o->unwrap ? "[./]" : "[ ]", o->derive_pub ? "[./]" : "[ ]", o->derive_priv ? "[./]" : "[ ]", o->always_auth ? "[./]" : "[ ]"); P11TEST_DATA_ROW(info, 14, '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 ? "EC_E" : o->key_type == CKK_EC_MONTGOMERY ? "EC_M" : o->key_type == CKK_AES ? "AES" : o->key_type == CKK_GENERIC_SECRET ? "GEN" : " ?? "), '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->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(" Public and Private key Derive Attributes -----------------------------'\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); } OpenSC-0.26.1/src/tests/p11test/p11test_case_usage.h000066400000000000000000000016331474147347300220330ustar00rootroot00000000000000/* * 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); OpenSC-0.26.1/src/tests/p11test/p11test_case_wait.c000066400000000000000000000036431474147347300216710ustar00rootroot00000000000000/* * 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); } OpenSC-0.26.1/src/tests/p11test/p11test_case_wait.h000066400000000000000000000015671474147347300217010ustar00rootroot00000000000000/* * 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); OpenSC-0.26.1/src/tests/p11test/p11test_case_wrap.c000066400000000000000000000516421474147347300217000ustar00rootroot00000000000000/* * 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); } OpenSC-0.26.1/src/tests/p11test/p11test_case_wrap.h000066400000000000000000000016371474147347300217040ustar00rootroot00000000000000/* * 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); OpenSC-0.26.1/src/tests/p11test/p11test_common.h000066400000000000000000000054571474147347300212340ustar00rootroot00000000000000/* * 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 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 */ OpenSC-0.26.1/src/tests/p11test/p11test_helpers.c000066400000000000000000000117061474147347300213730ustar00rootroot00000000000000/* * 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; } OpenSC-0.26.1/src/tests/p11test/p11test_helpers.h000066400000000000000000000023461474147347300214000ustar00rootroot00000000000000/* * 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 OpenSC-0.26.1/src/tests/p11test/p11test_loader.c000066400000000000000000000102011474147347300211640ustar00rootroot00000000000000/* * 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); } OpenSC-0.26.1/src/tests/p11test/p11test_loader.h000066400000000000000000000022261474147347300212010ustar00rootroot00000000000000/* * 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 OpenSC-0.26.1/src/tests/p11test/p11test_opensc.conf000066400000000000000000000005161474147347300217200ustar00rootroot00000000000000app 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/ } OpenSC-0.26.1/src/tests/p11test/piv_ref.json000066400000000000000000000772351474147347300205420ustar00rootroot00000000000000{ "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", "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" ], [ "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", "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" }] } OpenSC-0.26.1/src/tests/p11test/runtest.sh000077500000000000000000000171751474147347300202550ustar00rootroot00000000000000#!/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 "$@" OpenSC-0.26.1/src/tests/p11test/sc-hsm_ref.json000066400000000000000000000201721474147347300211220ustar00rootroot00000000000000{ "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" }] } OpenSC-0.26.1/src/tests/p11test/virt_cacard_ref.json000066400000000000000000001344731474147347300222230ustar00rootroot00000000000000{ "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", "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" }] } OpenSC-0.26.1/src/tests/p15dump.c000066400000000000000000000065441474147347300163460ustar00rootroot00000000000000/* Copyright (C) 2001 Juha Yrjölä * All rights reserved. * * PKCS#15 objects test */ #include "config.h" #include #include #include "libopensc/opensc.h" #include "libopensc/pkcs15.h" #include "sc-test.h" static struct sc_pkcs15_card *p15card; static int dump_objects(const char *what, int type) { struct sc_pkcs15_object **objs; int count, i; printf("\nEnumerating %s... ", what); fflush(stdout); if (SC_SUCCESS != sc_lock(card)) return 1; count = sc_pkcs15_get_objects(p15card, type, NULL, 0); if (count < 0) { printf("failed.\n"); fprintf(stderr, "Error enumerating %s: %s\n", what, sc_strerror(count)); sc_unlock(card); return 1; } if (count == 0) { printf("none found.\n"); if (SC_SUCCESS != sc_unlock(card)) return 1; return 0; } printf("%u found.\n", count); objs = calloc(count, sizeof(*objs)); if ((count = sc_pkcs15_get_objects(p15card, type, objs, count)) < 0) { fprintf(stderr, "Error enumerating %s: %s\n", what, sc_strerror(count)); } else { for (i = 0; i < count; i++) sc_test_print_object(objs[i]); } free(objs); if (SC_SUCCESS != sc_unlock(card)) return 1; return (count < 0) ? 1 : 0; } static int dump_unusedspace(void) { u8 *buf = NULL; size_t buf_len; sc_path_t path; sc_pkcs15_unusedspace_t *us; int r; if (p15card->file_unusedspace != NULL) path = p15card->file_unusedspace->path; else if (p15card->file_app != NULL) { path = p15card->file_app->path; sc_append_path_id(&path, (const u8 *) "\x50\x33", 2); } else { printf("\nCan't find unused space file.\n"); return -1; } path.count = -1; 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"); r = 0; } else printf("\nError reading file \"%s\": %s\n", sc_print_path(&path), sc_strerror(r)); goto err; } r = sc_pkcs15_parse_unusedspace(buf, buf_len, p15card); if (r != 0) { printf("\nError parsing EF(UnusedSpace): %s\n", sc_strerror(r)); goto err; } if (p15card->unusedspace_list == NULL) printf("\nEF(UnusedSpace) file is empty\n"); else { printf("\nContents of EF(UnusedSpace):\n"); for (us = p15card->unusedspace_list; us != NULL; us = us->next) printf(" - path=%s, index=%d, length=%d -- auth_id = %s\n", sc_print_path(&us->path), us->path.index, us->path.count, us->auth_id.len == 0 ? "" : sc_pkcs15_print_id(&us->auth_id)); } err: if (buf != NULL) free(buf); return r; } int main(int argc, char *argv[]) { int i; i = sc_test_init(&argc, argv); if (i < 0) return 1; printf("Looking for a PKCS#15 compatible Smart Card... "); fflush(stdout); if (SC_SUCCESS != sc_lock(card)) return 1; i = sc_pkcs15_bind(card, NULL, &p15card); /* 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"); sc_test_print_card(p15card); dump_objects("PIN codes", SC_PKCS15_TYPE_AUTH_PIN); dump_objects("Private keys", SC_PKCS15_TYPE_PRKEY); dump_objects("Public keys", SC_PKCS15_TYPE_PUBKEY); dump_objects("X.509 certificates", SC_PKCS15_TYPE_CERT_X509); dump_objects("data objects", SC_PKCS15_TYPE_DATA_OBJECT); dump_unusedspace(); sc_pkcs15_unbind(p15card); if (SC_SUCCESS != sc_unlock(card)) return 1; sc_test_cleanup(); return 0; } OpenSC-0.26.1/src/tests/pintest.c000066400000000000000000000060411474147347300165310ustar00rootroot00000000000000/* Copyright (C) 2001 Juha Yrjölä * All rights reserved. * * PKCS#15 PIN code test */ #include "config.h" #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include "libopensc/internal.h" #include "libopensc/opensc.h" #include "libopensc/pkcs15.h" #include "common/compat_getpass.h" #include "sc-test.h" static struct sc_pkcs15_card *p15card; static int enum_pins(struct sc_pkcs15_object ***ret) { struct sc_pkcs15_object **objs; int i, n; n = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_AUTH_PIN, NULL, 0); if (n < 0) { fprintf(stderr, "Error enumerating PIN codes: %s\n", sc_strerror(n)); return 1; } if (n == 0) { fprintf(stderr, "No PIN codes found!\n"); return 0; } objs = calloc(n, sizeof(*objs)); if (!objs) { fprintf(stderr, "Not enough memory!\n"); return 1; } if (0 > sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_AUTH_PIN, objs, n)) { fprintf(stderr, "Error enumerating PIN codes\n"); free(objs); return 1; } for (i = 0; i < n; i++) { sc_test_print_object(objs[i]); } *ret = objs; return n; } 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[(sizeof pin_obj->label) + 30]; u8 *pass; if (pin_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN) { printf("Skipping unblocking pin [%.*s]\n", (int) sizeof pin_obj->label, pin_obj->label); return 0; } 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)) return 1; i = sc_pkcs15_verify_pin(p15card, pin_obj, pass, strlen((char *) pass)); if (SC_SUCCESS != sc_unlock(card)) return 1; if (i) { if (i == SC_ERROR_PIN_CODE_INCORRECT) fprintf(stderr, "Incorrect PIN code (%d tries left)\n", pin_info->tries_left); else fprintf(stderr, "PIN verifying failed: %s\n", sc_strerror(i)); return 1; } else printf("PIN code correct.\n"); return 0; } int main(int argc, char *argv[]) { struct sc_pkcs15_object **objs = NULL; int i, count; i = sc_test_init(&argc, argv); if (i < 0) return 1; if (card->reader->capabilities & SC_READER_CAP_PIN_PAD) printf("Slot is capable of doing pinpad operations!\n"); printf("Looking for a PKCS#15 compatible Smart Card... "); fflush(stdout); if (SC_SUCCESS != sc_lock(card)) return 1; i = sc_pkcs15_bind(card, NULL, &p15card); if (SC_SUCCESS != sc_unlock(card)) return 1; if (i) { fprintf(stderr, "failed: %s\n", sc_strerror(i)); sc_test_cleanup(); return 1; } printf("found.\n"); printf("Enumerating PIN codes...\n"); if (SC_SUCCESS != sc_lock(card)) return 1; count = enum_pins(&objs); if (SC_SUCCESS != sc_unlock(card)) return 1; if (count < 0) { sc_pkcs15_unbind(p15card); sc_test_cleanup(); return 1; } for (i = 0; i < count; i++) { ask_and_verify_pin(objs[i]); } sc_pkcs15_unbind(p15card); sc_test_cleanup(); return 0; } OpenSC-0.26.1/src/tests/print.c000066400000000000000000000173011474147347300162000ustar00rootroot00000000000000/* Copyright (C) 2001, 2002 Juha Yrjölä * All rights reserved. * * PKCS#15 PIN code test */ #include "config.h" #include #include #include #include "libopensc/opensc.h" #include "libopensc/pkcs15.h" #include "sc-test.h" void sc_test_print_card(const sc_pkcs15_card_t *mycard) { const char *flags[] = { "Read-only", "Login required", "PRN generation", "EID compliant" }; int i, count = 0; assert(mycard != NULL); 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); printf("\tManufacturer ID: %s\n", mycard->tokeninfo->manufacturer_id); if (mycard->tokeninfo->preferred_language) printf("\tLanguage : %s\n", mycard->tokeninfo->preferred_language); printf("\tFlags : "); for (i = 0; i < 4; i++) { if ((mycard->tokeninfo->flags >> i) & 1) { if (count) printf(", "); printf("%s", flags[i]); count++; } } printf("\n"); } static void print_pin(const struct sc_pkcs15_object *obj) { const char *pin_flags[] = { "case-sensitive", "local", "change-disabled", "unblock-disabled", "initialized", "needs-padding", "unblockingPin", "soPin", "disable_allowed", "integrity-protected", "confidentiality-protected", "exchangeRefData" }; struct sc_pkcs15_auth_info *pin; const int pf_count = sizeof(pin_flags) / sizeof(pin_flags[0]); int i; pin = (struct sc_pkcs15_auth_info *) obj->data; printf("\tAuth ID : %s\n", sc_pkcs15_print_id(&pin->auth_id)); if (pin->auth_type == SC_PKCS15_PIN_AUTH_TYPE_PIN) { printf("\tFlags : [0x%02X]", pin->attrs.pin.flags); for (i = 0; i < pf_count; i++) if (pin->attrs.pin.flags & (1 << i)) { printf(", %s", pin_flags[i]); } printf("\n"); printf("\tLength : min_len:%lu, max_len:%lu, stored_len:%lu\n", (unsigned long) pin->attrs.pin.min_length, (unsigned long) pin->attrs.pin.max_length, (unsigned long) pin->attrs.pin.stored_length); printf("\tPad char : 0x%02X\n", pin->attrs.pin.pad_char); printf("\tReference : %d\n", pin->attrs.pin.reference); printf("\tEncoding : "); switch (pin->attrs.pin.type) { case SC_PKCS15_PIN_TYPE_BCD: printf("BCD\n"); break; case SC_PKCS15_PIN_TYPE_ASCII_NUMERIC: printf("ASCII-numeric\n"); break; case SC_PKCS15_PIN_TYPE_UTF8: printf("UTF8\n"); break; case SC_PKCS15_PIN_TYPE_HALFNIBBLE_BCD: printf("half-nibble BCD\n"); break; case SC_PKCS15_PIN_TYPE_ISO9564_1: printf("ISO 9564-1\n"); break; default: printf("[encoding %d]\n", pin->attrs.pin.type); } } if (pin->path.len) printf("\tPath : %s\n", sc_print_path(&pin->path)); if (pin->tries_left >= 0) printf("\tTries left : %d\n", pin->tries_left); } static void print_prkey(const struct sc_pkcs15_object *obj) { int i; size_t j; const char *usages[] = { "encrypt", "decrypt", "sign", "signRecover", "wrap", "unwrap", "verify", "verifyRecover", "derive", "nonRepudiation" }; const int usage_count = sizeof(usages) / sizeof(usages[0]); const char *access_flags[] = { "sensitive", "extract", "alwaysSensitive", "neverExtract", "local" }; const int af_count = sizeof(access_flags) / sizeof(access_flags[0]); struct sc_pkcs15_prkey_info *prkey; prkey = (struct sc_pkcs15_prkey_info *) obj->data; printf("\tUsage : [0x%X]", prkey->usage); for (i = 0; i < usage_count; i++) if (prkey->usage & (1 << i)) { printf(", %s", usages[i]); } printf("\n"); printf("\tAccess Flags: [0x%X]", prkey->access_flags); for (i = 0; i < af_count; i++) if (prkey->access_flags & (1 << i)) { printf(", %s", access_flags[i]); } printf("\n"); if (obj->type == SC_PKCS15_TYPE_PRKEY_RSA) 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"); if (prkey->path.len) { printf("\tPath : "); for (j = 0; j < prkey->path.len; j++) printf("%02X", prkey->path.value[j]); if (prkey->path.type == SC_PATH_TYPE_PATH_PROT) printf(" (protected)"); printf("\n"); } printf("\tID : %s\n", sc_pkcs15_print_id(&prkey->id)); } static void print_pubkey(const struct sc_pkcs15_object *obj) { int i; size_t j; const char *usages[] = { "encrypt", "decrypt", "sign", "signRecover", "wrap", "unwrap", "verify", "verifyRecover", "derive", "nonRepudiation" }; const int usage_count = sizeof(usages) / sizeof(usages[0]); const char *access_flags[] = { "sensitive", "extract", "alwaysSensitive", "neverExtract", "local" }; const int af_count = sizeof(access_flags) / sizeof(access_flags[0]); struct sc_pkcs15_pubkey_info *pubkey; pubkey = (struct sc_pkcs15_pubkey_info *) obj->data; printf("\tUsage : [0x%X]", pubkey->usage); for (i = 0; i < usage_count; i++) if (pubkey->usage & (1 << i)) { printf(", %s", usages[i]); } printf("\n"); printf("\tAccess Flags: [0x%X]", pubkey->access_flags); for (i = 0; i < af_count; i++) if (pubkey->access_flags & (1 << i)) { printf(", %s", access_flags[i]); } printf("\n"); if (obj->type == SC_PKCS15_TYPE_PUBKEY_RSA) printf("\tModLength : %lu\n", (unsigned long) pubkey->modulus_length); printf("\tKey ref : %d\n", pubkey->key_reference); printf("\tNative : %s\n", pubkey->native ? "yes" : "no"); printf("\tPath : "); for (j = 0; j < pubkey->path.len; j++) printf("%02X", pubkey->path.value[j]); printf("\n"); printf("\tID : %s\n", sc_pkcs15_print_id(&pubkey->id)); } static void print_cert_x509(const struct sc_pkcs15_object *obj) { struct sc_pkcs15_cert_info *cert; cert = (struct sc_pkcs15_cert_info *) obj->data; printf("\tAuthority : %s\n", cert->authority ? "yes" : "no"); printf("\tPath : %s\n", cert->path.len? sc_print_path(&cert->path) : ""); printf("\tID : %s\n", sc_pkcs15_print_id(&cert->id)); /* XXX original p15dump code would read the certificate * and dump the label */ } static void print_data_object_summary(const struct sc_pkcs15_object *obj) { struct sc_pkcs15_data_info *data_object; unsigned i; data_object = (struct sc_pkcs15_data_info *) obj->data; printf("\tPath : "); for (i = 0; i < data_object->path.len; i++) printf("%02X", data_object->path.value[i]); printf("\n"); printf("\tID : %s\n", sc_pkcs15_print_id(&data_object->id)); /* XXX original p15dump code would read the data object * and dump the label */ } void sc_test_print_object(const struct sc_pkcs15_object *obj) { const char *kind; void (*printer) (const struct sc_pkcs15_object *); switch (obj->type) { case SC_PKCS15_TYPE_AUTH_PIN: printer = print_pin; kind = "PIN"; break; case SC_PKCS15_TYPE_PRKEY_RSA: printer = print_prkey; kind = "Private RSA key"; break; case SC_PKCS15_TYPE_PUBKEY_RSA: printer = print_pubkey; kind = "Public RSA key"; break; case SC_PKCS15_TYPE_CERT_X509: printer = print_cert_x509; kind = "X.509 Certificate"; break; case SC_PKCS15_TYPE_DATA_OBJECT: printer = print_data_object_summary; kind = "Data Object"; break; default: printer = NULL; kind = "Something"; break; } printf("%s", kind); if (obj->label[0]) printf(" [%.*s]\n", (int) sizeof obj->label, obj->label); else printf(" (no label)\n"); printf("\tCom. Flags : "); switch (obj->flags) { case 0x01: printf("private\n"); break; case 0x02: printf("modifiable\n"); break; case 0x03: printf("private, modifiable\n"); break; default: printf("0x%X\n", obj->flags); } if (obj->auth_id.len) printf("\tCom. Auth ID: %s\n", sc_pkcs15_print_id(&obj->auth_id)); if (obj->user_consent) printf("\tUser consent: %u\n", obj->user_consent); if (printer) printer(obj); } OpenSC-0.26.1/src/tests/prngtest.c000066400000000000000000000025721474147347300167160ustar00rootroot00000000000000/* Copyright (C) 2001 Juha Yrjölä * All rights reserved. * * Pseudo-random number generator test program */ #include "config.h" #include #include #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_SYS_TIME_H #include #endif #include "libopensc/opensc.h" #include "sc-test.h" int main(int argc, char *argv[]) { struct timeval tv1, tv2; int i, c, cnt = 3, freq[256]; u8 buf[8]; i = sc_test_init(&argc, argv); if (i < 0) return 1; for (i = 0; i < 256; i++) freq[i] = 0; c = 0; while (cnt) { if ((c % 10) == 1) { printf("."); fflush(stdout); } if (c == 0) gettimeofday(&tv1, NULL); i = sc_get_challenge(card, buf, 8); if (i != 0) { fprintf(stderr, "sc_get_challenge() failed: %s\n", sc_strerror(i)); sc_test_cleanup(); return 1; } for (i = 0; i < 8; i++) freq[buf[i]]++; c++; if (c == 100) { unsigned long long foo, foo2; gettimeofday(&tv2, NULL); foo = tv2.tv_sec * 1000 + tv2.tv_usec / 1000; foo2 = tv1.tv_sec * 1000 + tv1.tv_usec / 1000; printf("\nTime to generate 64 bits of randomness: %lld ms\n", (foo - foo2) / 100); printf("Frequencies:\n"); for (i = 0; i < 256; i++) { if (i && (i & 0x07) == 0) printf("\n"); printf("%02X: %3d ", i, freq[i]); } printf("\n"); c = 0; cnt--; } } sc_test_cleanup(); return 0; } OpenSC-0.26.1/src/tests/regression/000077500000000000000000000000001474147347300170565ustar00rootroot00000000000000OpenSC-0.26.1/src/tests/regression/Makefile.am000066400000000000000000000006421474147347300211140ustar00rootroot00000000000000MAINTAINERCLEANFILES = $(srcdir)/Makefile.in dist_check_DATA = \ crypt0001 crypt0002 crypt0003 crypt0004 crypt0005 crypt0006 crypt0007 \ init0001 init0002 init0003 init0004 init0005 init0006 \ init0007 init0008 init0009 init0010 init0011 init0012 \ pin0001 pin0002 \ README test.p12 bintest dist_check_SCRIPTS = erase functions run-all # remove log files from regression tests clean-local: -rm -rf out failed OpenSC-0.26.1/src/tests/regression/README000066400000000000000000000027111474147347300177370ustar00rootroot00000000000000 This directory contains regression test scripts. Note this is still work in progress, hopefully we will add more scripts by and by. Run the test scripts from this directory. You need to have OpenSC fully built in order for them to do anything useful. All test scripts accept the following set of arguments --use-default-transport-keys if your card requires a transport key in pkcs15-init (for instance, the GPK and Cryptoflex do), and the default transport key as determined by OpenSC works fine. [If it doesn't please get in contact with us!] --reader N Use the specified reader *** ATTENTION *** Some cards require that you present one or several PINs when erasing them. That is because these cards to not support a native mechanism for erasing the card. In this case, OpenSC will perform a recursive removal of files, pretty much like a "rm -rf" in Unix. As some of these files are PIN protected against deletion, we have to present the PIN before being allowed to do so. For this reason, the tests may ask you for various PINs. When asking for the SO PIN, the prompt will always refer to the "Security Officer PIN". Any other prompts (Test User PIN, etc) refer to the user PIN. All tests use the same PINs: 999999 as the SO PIN, if one is used 888888 as the SO PUK, if one is used 0000 as the user PIN, if one is used 111111 as the user PUK, if one is used Some tests will install more than one user PIN, but they will all have the same value. OpenSC-0.26.1/src/tests/regression/bintest000066400000000000000000000010001474147347300204400ustar00rootroot00000000000000?H%jj3yju߄M+SnrEKIΙD^֖0FSu-ҵ>K2 V-LFSxڢj_7(7 x>s8e%ItC{R,f(^ؒZ;t:Zߧ.0Y ~ ͕\s[ cwq*Flv). AB۠moZHuۢdXCVOpenSC-0.26.1/src/tests/regression/crypt0001000077500000000000000000000017751474147347300204600ustar00rootroot00000000000000#!/bin/bash # # This test checks various aspects of RSA signature generation # # It needs a card with a private key+certificate pair at ID 45 # # Run this from the regression test directory. . functions msg < $m msg "Signing and verifying using MD5" run_check_status openssl dgst -md5 -binary -out $d < $m p15_crypt -s --md5 --pkcs1 -i $d -o $s run_check_output "Verified OK" \ openssl dgst -verify $p -md5 -signature $s < $m success msg "Signing and verifying using SHA1" run_check_status openssl dgst -sha1 -binary -out $d < $m p15_crypt -s --sha-1 --pkcs1 -i $d -o $s run_check_output "Verified OK" \ openssl dgst -verify $p -sha1 -signature $s < $m success p15_erase --secret @01=0000 OpenSC-0.26.1/src/tests/regression/crypt0002000077500000000000000000000014221474147347300204460ustar00rootroot00000000000000#!/bin/bash # # This test checks various aspects of RSA decryption # # It needs a card with a private key+certificate pair at ID 45 # # Run this from the regression test directory. . functions msg < $o run_check_status openssl rsautl -pubin -inkey $p -encrypt -in $o -out $e p15_crypt -c --pkcs1 -i $e -o $d cmp $o $d || fail "Decrypted file does not match plain text file" success p15_erase --secret @01=0000 OpenSC-0.26.1/src/tests/regression/crypt0003000077500000000000000000000022151474147347300204500ustar00rootroot00000000000000#!/bin/bash # # This test checks various aspects of RSA decryption # # It will blank the card, create an RSA key and test it # # Run this from the regression test directory. . functions msg < $m msg "Signing and verifying using MD5" run_check_status openssl dgst -md5 -binary -out $d < $m p15_crypt -s --md5 --pkcs1 -i $d -o $s run_check_output "Verified OK" \ openssl dgst -verify $p -md5 -signature $s < $m success msg "Signing and verifying using SHA1" run_check_status openssl dgst -sha1 -binary -out $d < $m p15_crypt -s --sha-1 --pkcs1 -i $d -o $s run_check_output "Verified OK" \ openssl dgst -verify $p -sha1 -signature $s < $m success p15_erase --secret @01=0000 OpenSC-0.26.1/src/tests/regression/crypt0004000077500000000000000000000021161474147347300204510ustar00rootroot00000000000000#!/bin/bash # # This test checks various aspects of RSA signing # # It will blank the card, create an RSA key and test it # # Run this from the regression test directory. . functions msg < $m msg "Signing and verifying using MD5" run_check_status openssl dgst -md5 -binary -out $d < $m p15_crypt -s --md5 --pkcs1 -i $d -o $s run_check_output "Verified OK" \ openssl dgst -verify $p -md5 -signature $s < $m success msg "Signing and verifying using SHA1" run_check_status openssl dgst -sha1 -binary -out $d < $m p15_crypt -s --sha-1 --pkcs1 -i $d -o $s run_check_output "Verified OK" \ openssl dgst -verify $p -sha1 -signature $s < $m success p15_erase --secret @01=0000 OpenSC-0.26.1/src/tests/regression/crypt0005000077500000000000000000000036021474147347300204530ustar00rootroot00000000000000#!/bin/bash # # This test checks various aspects of RSA signature generation # # It needs a card with a private key+certificate pair at ID 45 # # Run this from the regression test directory. . functions msg < $m msg "Signing and verifying using SHA1" run_check_status openssl dgst -sha1 -binary -out $d < $m p15_crypt -s --sha-1 --pkcs1 -i $d -o $s run_check_output "Verified OK" \ openssl dgst -verify $p -sha1 -signature $s < $m success else msg "" msg "The card doesn't seem to support 2048 bit RSA key generation." msg "Skipping test !" msg "" fi p15_erase --secret @01=0000 msg < $m msg "Signing and verifying using SHA1" run_check_status openssl dgst -sha1 -binary -out $d < $m p15_crypt -s --sha-1 --pkcs1 -i $d -o $s run_check_output "Verified OK" \ openssl dgst -verify $p -sha1 -signature $s < $m success else msg "" msg "The card doesn't seem to support 2048 bit RSA keys." msg "Skipping test !" msg "" fi p15_erase --secret @01=0000 OpenSC-0.26.1/src/tests/regression/crypt0006000077500000000000000000000035111474147347300204530ustar00rootroot00000000000000#!/bin/bash # # This test checks various aspects of RSA decryption # # It needs a card with a private key+certificate pair at ID 45 # # Run this from the regression test directory. . functions msg < $o run_check_status openssl rsautl -pubin -inkey $p -encrypt -in $o -out $e p15_crypt -c --pkcs1 -i $e -o $d cmp $o $d || fail "Decrypted file does not match plain text file" success else msg "" msg "The card doesn't seem to support 2048 bit RSA key generation." msg "Skipping test !" msg "" fi p15_erase --secret @01=0000 msg < $o run_check_status openssl rsautl -pubin -inkey $p -encrypt -in $o -out $e p15_crypt -c --pkcs1 -i $e -o $d cmp $o $d || fail "Decrypted file does not match plain text file" success else msg "" msg "The card doesn't seem to support 2048 bit RSA keys." msg "Skipping test !" msg "" fi p15_erase --secret @01=0000 OpenSC-0.26.1/src/tests/regression/crypt0007000077500000000000000000000035401474147347300204560ustar00rootroot00000000000000#!/bin/bash # # This test checks various aspects of RSA decryption # # It needs a card with a private key+certificate pair at ID 45 # # Run this from the regression test directory. . functions msg <&2 exit 1 done fi # Eat any arguments given on the command line while [ $# -ne 0 ]; do case $1 in --*) var=`expr "$1" : '--\(.*\)'|tr - _` eval opt_$var=true;; esac case $1 in -T|--use-default-transport-keys|\ --no-prompt|\ --soft|\ -v*) p15init="$p15init $1";; --reader) P15_READER=$2 shift;; *) echo "Unexpected option $1" >&2 exit 1;; esac shift done if test "$P15_READER"; then p15crypt="$p15crypt --reader $P15_READER" p15tool="$p15tool --reader $P15_READER" p15init="$p15init --reader $P15_READER" osctool="$osctool --reader $P15_READER" fi # Get terminal control sequences if false && tty >/dev/null 2>&1; then __red=`tput setaf 1` __green=`tput setaf 2` __black=`tput setaf 0` else __red= __green= __black= fi test_failed=false function atexit { if ! $test_failed; then test "$p15temp" && rm -rf $p15temp msg <<-EOF ::: ::: ${__green}Test set completed successfully${__black} ::: EOF fi } mkdir -p $p15temp trap atexit 0 1 2 13 15 # Redirect output to log file, but keep copies of # stdout/stderr descriptors on fd 3 and 4 exec 3>&1 4>&2 >$p15log 2>&1 fi # Clobber log file cp /dev/null $p15log function msg { if [ $# -eq 0 ]; then # This is a here script cat >&3 else echo "::: $*" >&3 fi } function yesno { while true; do echo -n "$* [y/n]" >&3 read -n 1 ans echo >&3 case $ans in [yY]) return 0;; [nN]) return 1;; esac echo "*** Answer must be y or n" done } function fail { ( echo "*** ${__red}$*${__black}" if [ -f $p15log ]; then echo "--- Command output ---" cat $p15log fi echo "--- Test files left in $p15temp ---" ls -a $p15temp ) >&4 test_failed=true trap "" exit 1 } function error { echo "*** $*" >&4 } function fatal { echo "*** $*" >&4 exit 1; } function success { msg "SUCCESS" } function run_display_output { run_check_status "$@" >&3 2>&4 return $? } function run_check_status { echo ":::::: run_check_status $*" >&3 cp /dev/null $p15log if ! "$@" 2> $terrlog; then if [ -n "$suppress_error_msg" ] && grep "$suppress_error_msg" $terrlog &> /dev/null ; then msg "The card does not supported the request feature." unset suppress_error_msg return 1 else cat $terrlog fail "Command failed (status code $?): $*" fi fi } function run_check_output { msg=$1 shift echo ":::::: run_check_output \"$1\" $*" >&3 cp /dev/null $p15log out=`eval "$@" 2>&1` # Make sure output makes it to log file echo $out case $out in "$msg") return 0;; *) fail "Command failed (expected $msg): $*";; esac } function skip_if_card { name=`$osctool --name` for __pat in "$@"; do if expr "$name" : "${__pat}.*" >/dev/null; then msg "Detected $name; skipping test" exit 0 fi done } function skip_unless_card { name=`$osctool --name` for __pat in "$@"; do if expr "$name" : "${__pat}.*" >/dev/null; then return fi done msg "Detected $name; skipping test" exit 1 } ################################################################## # # Common pkcs15 functions # ################################################################## function p15_init { msg <<-EOF ::: ::: Testing pkcs15-init ::: ::: The PINs used by this test script (if applicable) are ::: Test SO PIN 999999 ::: Test User PIN 0000 ::: EOF $p15init --assert-pristine || fail "This test requires a clean card, please erase existing pkcs15 structure" msg "Initializing card ($*)" run_display_output $p15init -C \ --label "OpenSC Test Card" \ --serial DEADBEEF \ $* >&3 >&4 success } function p15_erase { msg "Erasing card ($*)" run_display_output $p15init --erase-card \ --secret @FF=999999 \ "$@" >&3 >&4 success } function p15_set_pin { msg "Setting user PIN ($*)" run_display_output $p15init -P \ --label "Test User PIN" \ --pin "0000" --puk "111111" \ "$@" success } function p15_change_pin { msg "Changing user PIN ($*)" run_display_output $p15tool \ --change-pin \ --pin 0000 \ --new-pin 2222 \ "$@" success } function p15_unblock_pin { msg "Changing user PIN ($*)" run_display_output $p15tool \ --unblock-pin \ --puk 111111 \ --new-pin 2222 \ "$@" success } function p15_gen_key { type=$1 shift msg "Generating key ($*)" if run_display_output $p15init -G $type \ --pin 0000 \ --id 45 \ --label "Test User Key" \ "$@" ; then success else return $? fi } function p15_exp_key { keyfile=$1 shift msg "Generating key ($*)" run_display_output $p15tool \ --pin 0000 \ --read-public-key 45 \ --output $p15temp/$keyfile \ "$@" success } function p15_store_key { keyfile=$1 shift msg "Storing private key $keyfile ($*)" if run_display_output $p15init -S $keyfile \ --pin 0000 \ --id 45 \ --label "Test User Key" \ "$@" ; then success else return $? fi } function p15_crypt { run_check_status $p15crypt \ --pin 0000 \ "$@" } function p15_validate { msg "Card contents according to p15tool --dump" run_display_output $p15tool --dump < /dev/null msg "Validating card using pkcs11-tool" run_display_output $p11tool -t --login --pin 0000 \ --module $p11module \ --token-label "OpenSC Test Card" $* < /dev/null success } OpenSC-0.26.1/src/tests/regression/init0001000077500000000000000000000003451474147347300202520ustar00rootroot00000000000000#!/bin/bash # # Test pkcs15-init # # Run this from the regression test directory. . functions p15_init --no-so-pin p15_set_pin -a 01 p15_gen_key rsa/1024 -a 01 --key-usage sign,decrypt p15_validate p15_erase --secret @01=0000 OpenSC-0.26.1/src/tests/regression/init0002000077500000000000000000000005131474147347300202500ustar00rootroot00000000000000#!/bin/bash # # Test pkcs15-init # # Run this from the regression test directory. . functions # skip_if_card Cryptoflex Cyberflex Multiflex p15_init --so-pin 999999 --so-puk 88888888 p15_set_pin -a 27 --so-pin 999999 p15_gen_key rsa/1024 -a 27 --so-pin 999999 --key-usage sign,decrypt p15_validate p15_erase --secret @27=0000 OpenSC-0.26.1/src/tests/regression/init0003000077500000000000000000000003411474147347300202500ustar00rootroot00000000000000#!/bin/bash # # Test pkcs15-init # # Run this from the regression test directory. . functions p15_init --no-so-pin p15_set_pin -a 01 p15_gen_key rsa/1024 --key-usage decrypt -a 01 p15_validate p15_erase --secret @01=0000 OpenSC-0.26.1/src/tests/regression/init0004000077500000000000000000000003351474147347300202540ustar00rootroot00000000000000#!/bin/bash # # Test pkcs15-init # # Run this from the regression test directory. . functions p15_init --no-so-pin p15_set_pin -a 01 p15_gen_key rsa/1024 -a 01 --key-usage sign p15_validate p15_erase --secret @01=0000 OpenSC-0.26.1/src/tests/regression/init0005000077500000000000000000000003451474147347300202560ustar00rootroot00000000000000#!/bin/bash # # Test pkcs15-init # # Run this from the regression test directory. . functions p15_init --no-so-pin p15_set_pin -a 01 p15_gen_key rsa/1024 -a 01 --key-usage sign,decrypt p15_validate p15_erase --secret @01=0000 OpenSC-0.26.1/src/tests/regression/init0006000077500000000000000000000004751474147347300202630ustar00rootroot00000000000000#!/bin/bash # # Test pkcs15-init # # Run this from the regression test directory. . functions k=$p15temp/private.pem p15_init --no-so-pin p15_set_pin -a 01 msg "Generating key with OpenSSL" run_check_status openssl genrsa -out $k -f4 1024 success p15_store_key $k -a 01 p15_validate p15_erase --secret @01=0000 OpenSC-0.26.1/src/tests/regression/init0007000077500000000000000000000006121474147347300202550ustar00rootroot00000000000000#!/bin/bash # # Test pkcs15-init # # Run this from the regression test directory. . functions p15_init --no-so-pin p15_set_pin -a 01 p15_set_pin -a 02 --label "User Signature PIN" p15_gen_key rsa/512 -a 01 --key-usage sign,decrypt p15_gen_key rsa/512 -a 02 --key-usage nonRepudiation \ --id feeb \ --label "Non-Repudiation Key" p15_validate p15_erase --secret @01=0000 --secret @02=0000 OpenSC-0.26.1/src/tests/regression/init0008000077500000000000000000000003661474147347300202640ustar00rootroot00000000000000#!/bin/bash # # Test pkcs15-init # # Run this from the regression test directory. . functions p15_init --no-so-pin p15_set_pin -a 01 p15_store_key test.p12 --format pkcs12 --passphrase "password" -a 01 p15_validate p15_erase --secret @01=0000 OpenSC-0.26.1/src/tests/regression/init0009000077500000000000000000000012231474147347300202560ustar00rootroot00000000000000#!/bin/bash # # Test pkcs15-init # # Run this from the regression test directory. . functions p15_init --no-so-pin p15_set_pin -a 01 p15_gen_key rsa/1024 -a 01 --key-usage decrypt p15_exp_key key.pem msg "Encrypting message (pkcs1 padding)" echo lalla > $p15temp/message run_check_status openssl rsautl -encrypt \ -pubin -inkey $p15temp/key.pem \ -in $p15temp/message \ -out $p15temp/encrypted run_check_status $p15crypt --decipher --pkcs1 \ --input $p15temp/encrypted \ --output $p15temp/decrypted \ --pin 0000 cmp $p15temp/message $p15temp/decrypted \ || fail "Decrypted file does not match plain text file" success p15_erase --secret @01=0000 OpenSC-0.26.1/src/tests/regression/init0010000077500000000000000000000013151474147347300202500ustar00rootroot00000000000000#!/bin/bash # # Test pkcs15-init # # Run this from the regression test directory. . functions p15_init --no-so-pin p15_set_pin -a 01 p15_gen_key rsa/1024 -a 01 --key-usage sign p15_exp_key key.pem echo lalla > $p15temp/message msg "Digesting the message" run_check_status openssl dgst -md5 \ -binary -out $p15temp/md5value \ $p15temp/message msg "Signing message (with key)" run_check_status $p15crypt --sign --md5 --pkcs1 \ --input $p15temp/md5value \ --output $p15temp/signature \ --pin 0000 msg "Verifying message (with software)" run_check_output "Verified OK" openssl dgst -md5 \ -verify $p15temp/key.pem \ -signature $p15temp/signature \ $p15temp/message success p15_erase --secret @01=0000 OpenSC-0.26.1/src/tests/regression/init0011000077500000000000000000000013231474147347300202500ustar00rootroot00000000000000#!/bin/bash # # Test pkcs15-init # # Run this from the regression test directory. . functions p15_init --no-so-pin p15_set_pin -a 01 p15_gen_key rsa/1024 -a 01 --key-usage sign p15_exp_key key.pem echo lalla > $p15temp/message msg "Digesting the message" run_check_status openssl dgst -sha1 \ -binary -out $p15temp/sha1value \ $p15temp/message msg "Signing message (with key)" run_check_status $p15crypt --sign --sha-1 --pkcs1 \ --input $p15temp/sha1value \ --output $p15temp/signature \ --pin 0000 msg "Verifying message (with software)" run_check_output "Verified OK" openssl dgst -sha1 \ -verify $p15temp/key.pem \ -signature $p15temp/signature \ $p15temp/message success p15_erase --secret @01=0000 OpenSC-0.26.1/src/tests/regression/init0012000077500000000000000000000004251474147347300202530ustar00rootroot00000000000000#!/bin/bash # # Test pkcs15-init # # Run this from the regression test directory. . functions p15_init --profile pkcs15+onepin --pin 999999 --puk 111111 p15_gen_key rsa/1024 -a 01 --key-usage sign,decrypt --pin 999999 p15_validate --pin 999999 p15_erase --secret @01=999999 OpenSC-0.26.1/src/tests/regression/pin0001000077500000000000000000000002711474147347300200730ustar00rootroot00000000000000#!/bin/bash # # Test pkcs15-init # # Run this from the regression test directory. . functions p15_init --no-so-pin p15_set_pin -a 01 p15_change_pin -a 01 p15_erase --secret @01=2222 OpenSC-0.26.1/src/tests/regression/pin0002000077500000000000000000000002721474147347300200750ustar00rootroot00000000000000#!/bin/bash # # Test pkcs15-init # # Run this from the regression test directory. . functions p15_init --no-so-pin p15_set_pin -a 01 p15_unblock_pin -a 01 p15_erase --secret @01=2222 OpenSC-0.26.1/src/tests/regression/run-all000077500000000000000000000014741474147347300203640ustar00rootroot00000000000000#!/bin/bash mkdir -p out scripts="" options="" abort_if_fail=true while [ $# -gt 0 ]; do opt=$1; shift case $opt in --continue) abort_if_fail=false;; --reader) options="$options $opt $1" shift;; -*) options="$options $opt";; *) scripts="$scripts $opt";; esac done if [ -z "$scripts" ]; then scripts=`ls init* crypt* pin*` fi for script in $scripts; do echo -n "${script}... " mkdir -p test-data if ./$script $options >out/$script 2>&1; then echo "success" else mkdir -p failed failed="failed/$script" mv test-data $failed cp out/$script $failed/test.log echo "fail (test data moved to $failed)" if $abort_if_fail; then echo Aborting. exit 1 fi echo -n "Wiping card... " if ./erase $options >out/erase 2>&1; then echo done else echo failed. exit 1 fi fi done exit 0 OpenSC-0.26.1/src/tests/regression/test.p12000066400000000000000000000045351474147347300203700ustar00rootroot000000000000000 Y0  *H   0 0 *H 00 *H 0 *H  0د8;uqRU9biAQ׵waQ)">gAWc0N]K lx0lbѭ9 qf}b*܊Tuz,/h`1o"H8sAsя2M5 'm9n^)HB\^ja1X#”Ff)6MQYs9vQKs'b9ero܎ g*ƢvUYm/Dsm]+WY:wD113=н!;'/\ X{ Џ> Z l ūB )xLR=}3%}RX㯿.*$4뾋CuHj4~%Fq~9BQs/?=$P@8(UэBGO%tLva5vףRLh@ SED3①g?9(1dC^c_U.A^7!y i]7+f`2ԛ߰{}m_6s9@1ܺJobWktϸm'+!IV (˻Fk':2 '$?߾#==Q7)ONHBUB^wuXgdX}+ k:`S2vꞚS}9I\j @׵^kZacn6\v^yI$9jtfU\aHaQ=:Y᳾XGYїtE+i"Ϭ%F WVh}f3xsxmұ7uĪߧv:. ΏxWS[:]@\ y^vkC#p#+uh9OάA\t)ޜ$BE_&*)pJ?vfJ:b#e3Wկ.QߪS6ןK뽱ܒA\;%@SQ#EF֋fbpkqBbF|>&Ǐ?z_׏`{/ *qvl&K0.2w | ֽZFUX ƽK GQd NԪ]b'PbR" +ܾrå` ҫME:< [s%ja ɁU(vfr]+}Db80E>FK=%[2rveϓ8\(qLRDےE"w^qP1yKeEPȏ`7dj* 9WDk$`1ܯy ZQސ1Ӏǘ42F!?zܣ5aͮt~ }[AaUI^?۳{ U\6Oe1,%~y)ãZDW^/ă6ǖqU/Iu>y8$e%O(3T``:ϥVIZxzEsb'imw|8KԜʆ6u]0 *H 00 *H  00 *H  03VVj- arhc%VN/| D*P,< R)S52@RjPqtYH^'ik%4ukXgsoj&Y B{{֐eb&c1+шr;K~MkBlف,Si['*ĿH&x+mL=Zj&2҃J9EZCh6vG?{^ygP?qE&uF#UD'.EW[ȋWT-bX7:+ؙ >/3nFAWAҦ+R0ܴB?Z$R<{ hkm58Cgn(%-I?/,q^^Do|1-]<6 &]tQ:j#M[.٘H47w2DĿA6uWW,57mtB_(N[j,7ngXixdW8=kFkH:Ք#4KxSO(yh6R%$ ϗ{}S.Xaə1%0# *H  1ZîU@ztE010!0 +U-L(JWECe?OpenSC-0.26.1/src/tests/sc-test.c000066400000000000000000000057511474147347300164340ustar00rootroot00000000000000/* Copyright (C) 2001 Juha Yrjölä * All rights reserved. * * Common functions for test programs */ #include "config.h" #include #include #include #include #include "libopensc/opensc.h" #include "sc-test.h" sc_context_t *ctx; sc_card_t *card; static const struct option options[] = { { "reader", 1, NULL, 'r' }, { "driver", 1, NULL, 'c' }, { "debug", 0, NULL, 'd' }, { NULL, 0, NULL, 0 } }; int sc_test_init(int *argc, char *argv[]) { char *opt_driver = NULL, *app_name; int opt_debug = 0, opt_reader = -1; int i, c, rc; sc_context_param_t ctx_param; if ((app_name = strrchr(argv[0], '/')) != NULL) app_name++; else app_name = argv[0]; while ((c = getopt_long(*argc, argv, "r:c:d", options, NULL)) != -1) { switch (c) { case 'r': opt_reader = atoi(optarg); break; case 'c': opt_driver = optarg; break; case 'd': opt_debug++; break; default: fprintf(stderr, "usage: %s [-r reader] [-c driver] [-d]\n", app_name); exit(1); } } *argc = optind; printf("Using libopensc version %s.\n", sc_get_version()); memset(&ctx_param, 0, sizeof(ctx_param)); ctx_param.ver = 0; ctx_param.app_name = app_name; i = sc_context_create(&ctx, &ctx_param); if (i != SC_SUCCESS) { printf("Failed to establish context: %s\n", sc_strerror(i)); return i; } ctx->debug = opt_debug; if (opt_reader >= (int) sc_ctx_get_reader_count(ctx)) { fprintf(stderr, "Illegal reader number.\n" "Only %d reader(s) configured.\n", sc_ctx_get_reader_count(ctx)); exit(1); } while (1) { if (opt_reader >= 0) { rc = sc_detect_card_presence(sc_ctx_get_reader(ctx, opt_reader)); printf("Card %s.\n", rc == 1 ? "present" : "absent"); if (rc < 0) return rc; } else { 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 < 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; } printf("Please insert a smart card. Press return to continue"); fflush(stdout); while (getc(stdin) != '\n') ; } printf("Connecting... "); fflush(stdout); i = sc_connect_card(sc_ctx_get_reader(ctx, opt_reader), &card); if (i != SC_SUCCESS) { printf("Connecting to card failed: %s\n", sc_strerror(i)); return i; } printf("connected.\n"); { char tmp[SC_MAX_ATR_SIZE*3]; sc_bin_to_hex(card->atr.value, card->atr.len, tmp, sizeof(tmp) - 1, ':'); printf("ATR = %s\n",tmp); } if (opt_driver != NULL) { rc = sc_set_card_driver(ctx, opt_driver); if (rc != 0) { fprintf(stderr, "Driver '%s' not found!\n", opt_driver); return rc; } } return 0; } void sc_test_cleanup(void) { sc_disconnect_card(card); sc_release_context(ctx); } OpenSC-0.26.1/src/tests/sc-test.h000066400000000000000000000006661474147347300164410ustar00rootroot00000000000000#ifndef _SC_TEST_H #define _SC_TEST_H #include "libopensc/pkcs15.h" #ifdef __cplusplus extern "C" { #endif extern struct sc_context *ctx; extern struct sc_card *card; struct sc_pkcs15_card; struct sc_pkcs15_object; int sc_test_init(int *argc, char *argv[]); void sc_test_cleanup(void); void sc_test_print_card(const sc_pkcs15_card_t *); void sc_test_print_object(const struct sc_pkcs15_object *); #ifdef __cplusplus } #endif #endif OpenSC-0.26.1/src/tests/unittests/000077500000000000000000000000001474147347300167405ustar00rootroot00000000000000OpenSC-0.26.1/src/tests/unittests/Makefile.am000066400000000000000000000035451474147347300210030ustar00rootroot00000000000000MAINTAINERCLEANFILES = $(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='/usr/lib/x86_64-linux-gnu/libpcsclite.so.1' endif noinst_PROGRAMS = asn1 simpletlv cachedir pkcs15filter openpgp-tool hextobin \ decode_ecdsa_signature check_macro_reference_loop strip_pkcs1_2_padding \ base64 TESTS = asn1 simpletlv cachedir pkcs15filter openpgp-tool hextobin \ decode_ecdsa_signature check_macro_reference_loop strip_pkcs1_2_padding \ base64 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 \ $(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 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 OpenSC-0.26.1/src/tests/unittests/Makefile.mak000066400000000000000000000010301474147347300211410ustar00rootroot00000000000000TOPDIR = ..\..\.. TARGETS = asn1 compression pkcs15filter check_macro_reference_loop \ strip_pkcs1_2_padding base64 OBJECTS = asn1.obj \ compression.obj \ pkcs15-emulator-filter.obj \ check_macro_reference_loop.obj \ strip_pkcs1_2_padding.obj \ $(TOPDIR)\win32\versioninfo.res all: $(TARGETS) !INCLUDE $(TOPDIR)\win32\Make.rules.mak $(TARGETS): $(OBJECTS) $(LIBS) .c.exe: cl $(COPTS) /c $< link $(LINKFLAGS) /pdb:$*.pdb /out:$@ $*.obj $(OBJECTS) $(LIBS) if EXIST $@.manifest mt -manifest $@.manifest -outputresource:$@;1 OpenSC-0.26.1/src/tests/unittests/asn1.c000066400000000000000000000570711474147347300177600ustar00rootroot00000000000000/* * 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); } int main(void) { int rc; 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), }; rc = cmocka_run_group_tests(tests, NULL, NULL); return rc; } OpenSC-0.26.1/src/tests/unittests/base64.c000066400000000000000000000117121474147347300201720ustar00rootroot00000000000000/* * 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); }OpenSC-0.26.1/src/tests/unittests/cachedir.c000066400000000000000000000054411474147347300206520ustar00rootroot00000000000000/* * 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; } OpenSC-0.26.1/src/tests/unittests/check_macro_reference_loop.c000066400000000000000000000144441474147347300244200ustar00rootroot00000000000000/* * 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); } OpenSC-0.26.1/src/tests/unittests/compression.c000066400000000000000000000253721474147347300214560ustar00rootroot00000000000000/* * 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; } OpenSC-0.26.1/src/tests/unittests/decode_ecdsa_signature.c000066400000000000000000000137121474147347300235530ustar00rootroot00000000000000/* * 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); } OpenSC-0.26.1/src/tests/unittests/hextobin.c000066400000000000000000000063111474147347300207250ustar00rootroot00000000000000/* * 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; } OpenSC-0.26.1/src/tests/unittests/openpgp-tool.c000066400000000000000000000111431474147347300215270ustar00rootroot00000000000000/* * 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 "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); } OpenSC-0.26.1/src/tests/unittests/simpletlv.c000066400000000000000000000127411474147347300211300ustar00rootroot00000000000000/* * 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; } OpenSC-0.26.1/src/tests/unittests/sm.c000066400000000000000000000304551474147347300175320ustar00rootroot00000000000000/* * 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; } OpenSC-0.26.1/src/tests/unittests/strip_pkcs1_2_padding.c000066400000000000000000000145121474147347300232600ustar00rootroot00000000000000#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); } OpenSC-0.26.1/src/tests/unittests/torture.h000066400000000000000000000017431474147347300206220ustar00rootroot00000000000000/* * 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 */ OpenSC-0.26.1/src/tools/000077500000000000000000000000001474147347300146745ustar00rootroot00000000000000OpenSC-0.26.1/src/tools/Makefile.am000066400000000000000000000176471474147347300167470ustar00rootroot00000000000000include $(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,[@]DEFAULT_PKCS11_PROVIDER[@],"$(DEFAULT_PKCS11_PROVIDER)",g' \ -e 's,[@]PKCS11_REGISTER_SKIP_FIREFOX[@],$(PKCS11_REGISTER_SKIP_FIREFOX),g' \ -e 's,[@]VDFORMAT[@],$(VDFORMAT),g' \ -e 's,[@]X509DIR[@],$(X509DIR),g' MAINTAINERCLEANFILES = $(srcdir)/Makefile.in $(srcdir)/versioninfo-tools.rc $(srcdir)/versioninfo-opensc-notify.rc EXTRA_DIST = Makefile.mak versioninfo-tools.rc.in versioninfo-opensc-notify.rc.in 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 exe.manifest pkcs11-register.desktop.in org.opensc-project.mac.pkcs11-register.plist.in org.opensc-project.mac.opensc-notify.plist.in 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 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 if ENABLE_OPENSSL bin_PROGRAMS += cryptoflex-tool pkcs15-init netkey-tool piv-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_OPENPACE noinst_PROGRAMS = sceac-example endif # compile with $(PTHREAD_CFLAGS) to allow debugging with gdb 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 $(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 ../pkcs11/pkcs11-display.c ../pkcs11/pkcs11-display.h pkcs15_tool_LDADD = $(OPTIONAL_OPENSSL_LIBS) pkcs11_tool_SOURCES = pkcs11-tool.c util.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 cryptoflex_tool_LDADD = $(OPTIONAL_OPENSSL_LIBS) pkcs15_init_SOURCES = pkcs15-init.c util.c pkcs15_init_LDADD = $(OPTIONAL_OPENSSL_LIBS) cardos_tool_SOURCES = cardos-tool.c util.c cardos_tool_LDADD = $(OPTIONAL_OPENSSL_LIBS) eidenv_SOURCES = eidenv.c util.c 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-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 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 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 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 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 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 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 cardos_tool_SOURCES += versioninfo-tools.rc eidenv_SOURCES += versioninfo-tools.rc netkey_tool_SOURCES += versioninfo-tools.rc westcos_tool_SOURCES += versioninfo-tools.rc 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 $(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 OpenSC-0.26.1/src/tools/Makefile.mak000066400000000000000000000072101474147347300171030ustar00rootroot00000000000000TOPDIR = ..\.. default: all !INCLUDE $(TOPDIR)\win32\Make.rules.mak 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 \ 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 LIBS = $(TOPDIR)\src\common\common.lib \ $(TOPDIR)\src\scconf\scconf.lib \ $(TOPDIR)\src\libopensc\opensc.lib \ $(TOPDIR)\src\pkcs15init\pkcs15init.lib \ $(TOPDIR)\src\common\libpkcs11.lib \ $(TOPDIR)\src\common\libscdl.lib all: $(TARGETS) $(TARGETS): $(OBJECTS) $(LIBS) opensc-notify.exe: opensc-notify-cmdline.obj versioninfo-opensc-notify.res $(LIBS) cl $(COPTS) /c $*.c link $(LINKFLAGS) /pdb:$*.pdb /out:$@ $*.obj opensc-notify-cmdline.obj versioninfo-opensc-notify.res $(LIBS) gdi32.lib shell32.lib User32.lib ws2_32.lib shlwapi.lib mt -manifest exe.manifest -outputresource:$@;1 npa-tool.exe: npa-tool-cmdline.obj fread_to_eof.obj $(OBJECTS) $(LIBS) cl $(COPTS) /c $*.c link $(LINKFLAGS) /pdb:$*.pdb /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 mt -manifest exe.manifest -outputresource:$@;1 egk-tool.exe: egk-tool-cmdline.obj $(OBJECTS) $(LIBS) cl $(COPTS) /c $*.c link $(LINKFLAGS) /pdb:$*.pdb /out:$@ $*.obj egk-tool-cmdline.obj $(OBJECTS) $(LIBS) $(ZLIB_LIB) gdi32.lib shell32.lib User32.lib ws2_32.lib shlwapi.lib mt -manifest exe.manifest -outputresource:$@;1 goid-tool.exe: goid-tool-cmdline.obj fread_to_eof.obj $(OBJECTS) $(LIBS) cl $(COPTS) /c $*.c link $(LINKFLAGS) /pdb:$*.pdb /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 mt -manifest exe.manifest -outputresource:$@;1 opensc-asn1.exe: opensc-asn1-cmdline.obj fread_to_eof.obj versioninfo-tools.res $(LIBS) cl $(COPTS) /c $*.c link $(LINKFLAGS) /pdb:$*.pdb /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 mt -manifest exe.manifest -outputresource:$@;1 pkcs11-register.exe: pkcs11-register-cmdline.obj fread_to_eof.obj $(LIBS) cl $(COPTS) /c $*.c link $(LINKFLAGS) /pdb:$*.pdb /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 mt -manifest exe.manifest -outputresource:$@;1 pkcs15-tool.exe: pkcs15-tool.obj $(TOPDIR)\src\pkcs11\pkcs11-display.obj cl $(COPTS) /c $*.c link $(LINKFLAGS) /pdb:$*.pdb /out:$@ $*.obj $(TOPDIR)\src\pkcs11\pkcs11-display.obj $(OBJECTS) $(LIBS) $(OPENSSL_LIB) gdi32.lib shell32.lib User32.lib ws2_32.lib shlwapi.lib mt -manifest exe.manifest -outputresource:$@;1 openpgp-tool.exe: openpgp-tool-helpers.obj $(LIBS) cl $(COPTS) /c $*.c link $(LINKFLAGS) /pdb:$*.pdb /out:$@ $*.obj openpgp-tool-helpers.obj $(OBJECTS) $(LIBS) gdi32.lib shell32.lib User32.lib ws2_32.lib shlwapi.lib mt -manifest exe.manifest -outputresource:$@;1 sc-hsm-tool.exe: sc-hsm-tool.obj fread_to_eof.obj $(OBJECTS) $(LIBS) cl $(COPTS) /c $*.c link $(LINKFLAGS) /pdb:$*.pdb /out:$@ $*.obj sc-hsm-tool.obj fread_to_eof.obj $(OBJECTS) $(LIBS) $(OPENSSL_LIB) gdi32.lib shell32.lib User32.lib ws2_32.lib mt -manifest exe.manifest -outputresource:$@;1 .c.exe: cl $(COPTS) /c $< link $(LINKFLAGS) /pdb:$*.pdb /out:$@ $*.obj $(OBJECTS) $(LIBS) $(OPENSSL_LIB) gdi32.lib shell32.lib User32.lib ws2_32.lib shlwapi.lib mt -manifest exe.manifest -outputresource:$@;1 OpenSC-0.26.1/src/tools/cardos-tool.c000066400000000000000000000777541474147347300173120ustar00rootroot00000000000000/* * cardos-tool.c: Tool and Info about Card OS based tokens * * Copyright (C) 2008 Andreas Jellinghaus * Copyright (C) 2007 Jean-Pierre Szikora * Copyright (C) 2003 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include #include #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include #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; static int verbose = 0; static char *opt_reader = NULL; static const struct option options[] = { {"help", 0, NULL, 'h'}, {"info", 0, NULL, 'i'}, {"format", 0, NULL, 'f'}, {"startkey", 1, NULL, 's'}, {"change-startkey", 1, NULL, 'S'}, {"reader", 1, NULL, 'r'}, {"wait", 0, NULL, 'w'}, {"verbose", 0, NULL, 'v'}, {NULL, 0, NULL, 0} }; static const char *option_help[] = { "Print this help message", "Print information about this card", "Format this card erasing all content", "Specify startkey for format", "Change Startkey with given APDU command", "Uses reader number [0]", "Wait for a card to be inserted", "Verbose operation, may be used several times", }; static sc_context_t *ctx = NULL; static sc_card_t *card = NULL; static int check_apdu(const sc_apdu_t *apdu) { if (apdu->sw1 != 0x90 || apdu->sw2 != 0x00) { fprintf(stderr, "Some error occurred. Use '-v' several times to enable debug output."); return 1; } return 0; } static int cardos_info(void) { sc_apdu_t apdu; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; int is_cardos5 = 0; int r; 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); } memset(&apdu, 0, sizeof(apdu)); apdu.cla = 0x00; apdu.ins = 0xca; apdu.p1 = 0x01; apdu.p2 = 0x80; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.lc = 0; apdu.le = 256; apdu.cse = SC_APDU_CASE_2_SHORT; r = sc_transmit_apdu(card, &apdu); if (r) { fprintf(stderr, "APDU transmit failed: %s\n", sc_strerror(r)); return 1; } if (check_apdu(&apdu)) return 1; printf("Info : %s\n", apdu.resp); apdu.p2 = 0x82; apdu.resplen = sizeof(rbuf); r = sc_transmit_apdu(card, &apdu); if (r) { fprintf(stderr, "APDU transmit failed: %s\n", sc_strerror(r)); return 1; } if (check_apdu(&apdu)) return 1; if (apdu.resp[0] == 0xc9) is_cardos5 = 1; apdu.p2 = 0x81; apdu.resplen = sizeof(rbuf); r = sc_transmit_apdu(card, &apdu); if (r) { fprintf(stderr, "APDU transmit failed: %s\n", sc_strerror(r)); return 1; } if (check_apdu(&apdu)) return 1; if (is_cardos5) { printf("Serial number: %02x %02x %02x %02x %02x %02x %02x %02x\n", apdu.resp[0], apdu.resp[1], apdu.resp[2], apdu.resp[3], apdu.resp[4], apdu.resp[5], apdu.resp[6], apdu.resp[7]); } else { printf("Chip type: %d\n", apdu.resp[8]); printf("Serial number: %02x %02x %02x %02x %02x %02x\n", apdu.resp[10], apdu.resp[11], apdu.resp[12], apdu.resp[13], apdu.resp[14], apdu.resp[15]); printf("Full prom dump:\n"); if (apdu.resplen) util_hex_dump_asc(stdout, apdu.resp, apdu.resplen, -1); } apdu.p2 = 0x82; apdu.resplen = sizeof(rbuf); r = sc_transmit_apdu(card, &apdu); if (r) { fprintf(stderr, "APDU transmit failed: %s\n", sc_strerror(r)); return 1; } if (check_apdu(&apdu)) return 1; printf("OS Version: %d.%d", apdu.resp[0], apdu.resp[1]); if (apdu.resp[0] == 0xc8 && apdu.resp[1] == 0x02) { printf(" (that's CardOS M4.0)\n"); } else if (apdu.resp[0] == 0xc8 && apdu.resp[1] == 0x03) { printf(" (that's CardOS M4.01)\n"); } else if (apdu.resp[0] == 0xc8 && apdu.resp[1] == 0x04) { printf(" (that's CardOS M4.01a)\n"); } else if (apdu.resp[0] == 0xc8 && apdu.resp[1] == 0x06) { printf(" (that's CardOS M4.2)\n"); } else if (apdu.resp[0] == 0xc8 && apdu.resp[1] == 0x07) { printf(" (that's CardOS M4.3)\n"); } else if (apdu.resp[0] == 0xc8 && apdu.resp[1] == 0x08) { printf(" (that's CardOS M4.3B)\n"); } else if (apdu.resp[0] == 0xc8 && apdu.resp[1] == 0x09) { printf(" (that's CardOS M4.2B)\n"); } else if (apdu.resp[0] == 0xc8 && apdu.resp[1] == 0x0B) { printf(" (that's CardOS M4.2C)\n"); } else if (apdu.resp[0] == 0xc8 && apdu.resp[1] == 0x0D) { 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"); } apdu.p2 = 0x83; apdu.resplen = sizeof(rbuf); r = sc_transmit_apdu(card, &apdu); if (r) { fprintf(stderr, "APDU transmit failed: %s\n", sc_strerror(r)); return 1; } if (check_apdu(&apdu)) return 1; printf("Current life cycle: "); if (rbuf[0] == 0x34) { printf("%d (manufacturing)\n", rbuf[0]); } else if (rbuf[0] == 0x26) { if (is_cardos5) printf("%d (physinit)\n", rbuf[0]); else printf("%d (initialization)\n", rbuf[0]); } else if (rbuf[0] == 0x23) { printf("%d (physpers)\n", rbuf[0]); } else if (rbuf[0] == 0x24) { printf("%d (personalization)\n", rbuf[0]); } else if (rbuf[0] == 0x20) { printf("%d (administration)\n", rbuf[0]); } else if (rbuf[0] == 0x10) { printf("%d (operational)\n", rbuf[0]); } else if (rbuf[0] == 0x29) { printf("%d (erase in progress)\n", rbuf[0]); } else if (rbuf[0] == 0x3F) { printf("%d (death)\n", rbuf[0]); } else { printf("%d (unknown)\n", rbuf[0]); } apdu.p2 = 0x84; apdu.resplen = sizeof(rbuf); r = sc_transmit_apdu(card, &apdu); if (r) { fprintf(stderr, "APDU transmit failed: %s\n", sc_strerror(r)); return 1; } if (check_apdu(&apdu)) return 1; printf("Security Status of current DF:\n"); util_hex_dump_asc(stdout, apdu.resp, apdu.resplen, -1); apdu.p2 = 0x85; apdu.resplen = sizeof(rbuf); r = sc_transmit_apdu(card, &apdu); if (r) { fprintf(stderr, "APDU transmit failed: %s\n", sc_strerror(r)); return 1; } if (check_apdu(&apdu)) return 1; printf("Free memory : %d\n", rbuf[0]<<8|rbuf[1]); apdu.p2 = 0x86; apdu.resplen = sizeof(rbuf); r = sc_transmit_apdu(card, &apdu); if (r) { fprintf(stderr, "APDU transmit failed: %s\n", sc_strerror(r)); return 1; } if (check_apdu(&apdu)) return 1; if (rbuf[0] == 0x00) { printf("ATR Status: 0x%d ROM-ATR\n",rbuf[0]); } else if (rbuf[0] == 0x80) { printf("ATR Status: 0x%d EEPROM-ATR\n",rbuf[0]); } else { printf("ATR Status: 0x%d unknown\n",rbuf[0]); } apdu.p2 = 0x88; apdu.resplen = sizeof(rbuf); r = sc_transmit_apdu(card, &apdu); if (r) { fprintf(stderr, "APDU transmit failed: %s\n", sc_strerror(r)); return 1; } if (check_apdu(&apdu)) return 1; printf("Packages installed:\n"); util_hex_dump_asc(stdout, apdu.resp, apdu.resplen, -1); apdu.p2 = 0x89; apdu.resplen = sizeof(rbuf); r = sc_transmit_apdu(card, &apdu); if (r) { fprintf(stderr, "APDU transmit failed: %s\n", sc_strerror(r)); return 1; } if (check_apdu(&apdu)) return 1; if (is_cardos5) printf("Ram size: %d, Eeprom size: %d, cpu type: %x, chip config: %d, chip manufacturer: %d\n", rbuf[0]<<8|rbuf[1], rbuf[2]<<8|rbuf[3], rbuf[4], rbuf[6], rbuf[7]); else printf("Ram size: %d, Eeprom size: %d, cpu type: %x, chip config: %d\n", rbuf[0]<<8|rbuf[1], rbuf[2]<<8|rbuf[3], rbuf[4], rbuf[5]); apdu.p2 = 0x8a; apdu.resplen = sizeof(rbuf); r = sc_transmit_apdu(card, &apdu); if (r) { fprintf(stderr, "APDU transmit failed: %s\n", sc_strerror(r)); return 1; } if (check_apdu(&apdu)) return 1; if (is_cardos5) printf("Free eeprom memory: %d\n", rbuf[0]<<24|rbuf[1]<<16|rbuf[2]<<8|rbuf[3]); else printf("Free eeprom memory: %d\n", rbuf[0]<<8|rbuf[1]); apdu.p2 = 0x8d; apdu.resplen = sizeof(rbuf); r = sc_transmit_apdu(card, &apdu); if (r) { fprintf(stderr, "APDU transmit failed: %s\n", sc_strerror(r)); return 1; } if (check_apdu(&apdu)) return 1; printf("Current Maximum Data Field Length: %d\n", rbuf[0]<<8|rbuf[1]); if (is_cardos5) { apdu.p2 = 0x8B; apdu.resplen = sizeof(rbuf); r = sc_transmit_apdu(card, &apdu); if (r) { fprintf(stderr, "APDU transmit failed: %s\n", sc_strerror(r)); return 1; } if (check_apdu(&apdu)) return 1; printf("Complete chip production data:\n"); util_hex_dump_asc(stdout, apdu.resp, apdu.resplen, -1); } apdu.p2 = 0x96; apdu.resplen = sizeof(rbuf); r = sc_transmit_apdu(card, &apdu); if (r) { fprintf(stderr, "APDU transmit failed: %s\n", sc_strerror(r)); return 1; } if (check_apdu(&apdu)) return 1; printf("System keys: PackageLoadKey (version 0x%02x, retries %d)\n", rbuf[0], rbuf[1]); printf("System keys: StartKey (version 0x%02x, retries %d)\n", rbuf[2], rbuf[3]); apdu.p2 = 0x87; apdu.resplen = sizeof(rbuf); r = sc_transmit_apdu(card, &apdu); if (r) { fprintf(stderr, "APDU transmit failed: %s\n", sc_strerror(r)); return 1; } if (check_apdu(&apdu)) return 1; printf("Path to current DF:\n"); util_hex_dump_asc(stdout, apdu.resp, apdu.resplen, -1); return 0; } #ifdef ENABLE_OPENSSL static int cardos_sm4h(const unsigned char *in, size_t inlen, unsigned char *out, size_t outlen, const unsigned char *key, size_t keylen) { /* using a buffer with an APDU, build an SM 4h APDU for cardos */ int plain_lc; /* data size in orig APDU */ unsigned int mac_input_len, enc_input_len; unsigned char *mac_input, *enc_input; 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", keylen); return 0; } if (inlen < 4) return 0; /* failed, apdu too short */ if (inlen <= 5) plain_lc = 0; if (inlen > 5) plain_lc = in[4]; /* 4 + plain_lc plus 0..7 bytes of padding */ mac_input_len = 4 + plain_lc; while (mac_input_len % 8) mac_input_len++; mac_input = calloc(1,mac_input_len); if (!mac_input) { printf("out of memory, aborting\n"); return 0; } mac_input[0] = in[1]; /* ins */ mac_input[1] = in[2]; /* p1 */ mac_input[2] = in[3]; /* p2 */ mac_input[3] = plain_lc + 8; if (plain_lc) /* copy data from in APDU */ memcpy(&mac_input[4],&in[5],plain_lc); /* calloc already did the ansi padding: rest is 00 */ /* 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; 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]; 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]; 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]; 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) */ enc_input_len = plain_lc + 8 + 1; while (enc_input_len % 8) enc_input_len++; enc_input = calloc(1,enc_input_len); if (!enc_input) { free(mac_input); printf("out of memory, aborting\n"); return 0; } if (plain_lc) 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 cleared the remaining bytes to 00 */ if (outlen < 5 + enc_input_len) { free(mac_input); free(enc_input); printf("output buffer too small, aborting.\n"); return 0; } out[0] = in[0]; /* cla */ out[1] = in[1]; /* ins */ out[2] = in[2]; /* p1 */ out[3] = in[3]; /* p2 */ 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 (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 (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 (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); printf ("Encrypted APDU:\n"); util_hex_dump_asc(stdout, out, out[4] + 5, -1); printf ("\n"); } free(mac_input); free(enc_input); return 1; } #endif #ifdef ENABLE_OPENSSL static int cardos_format(const char *opt_startkey) { unsigned const char startkey[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; sc_apdu_t apdu; u8 rbuf[256]; int r; if (opt_startkey) { fprintf(stderr, "startkey option not implemented yet, aborting!\n"); return 1; /* TODO: instead validate/parse opt_startkey into startkey */ /* format would be ii:vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv */ /* with "ii" the startkey index as hex number and */ /* "vv" the 16 byte value in hex (32 chars) */ } if (verbose) { printf ("StartKey:\n"); util_hex_dump_asc(stdout, startkey, 16, -1); } /* use GET DATA for version - 00 ca 01 82 * returns e.g. c8 09 for 4.2B */ memset(&apdu, 0, sizeof(apdu)); apdu.cla = 0x00; apdu.ins = 0xca; apdu.p1 = 0x01; apdu.p2 = 0x82; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.lc = 0; apdu.le = 256; apdu.cse = SC_APDU_CASE_2_SHORT; r = sc_transmit_apdu(card, &apdu); if (r) { fprintf(stderr, "APDU transmit failed: %s\n", sc_strerror(r)); return 1; } if (check_apdu(&apdu)) return 1; if (apdu.resplen != 0x02) { printf("did not receive version info, aborting\n"); return 1; } if ((rbuf[0] != 0xc8 || rbuf[1] != 0x09) && /* M4.2B */ (rbuf[0] != 0xc8 || rbuf[1] != 0x08) && /* M4.3B */ (rbuf[0] != 0xc8 || rbuf[1] != 0x0B) && /* M4.2C */ (rbuf[0] != 0xc8 || rbuf[1] != 0x0D)) { /* M4.4 */ printf("currently only CardOS M4.2B, M4.2C, M4.3B and M4.4 are supported, aborting\n"); return 1; } /* GET DATA for startkey index - 00 ca 01 96 * returns 6 bytes PackageLoadKey.Version, PackageLoadKey.Retry * Startkey.Version, Startkey.Retry, 2 internal data byes */ memset(&apdu, 0, sizeof(apdu)); apdu.cla = 0x00; apdu.ins = 0xca; apdu.p1 = 0x01; apdu.p2 = 0x96; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.lc = 0; apdu.le = 256; apdu.cse = SC_APDU_CASE_2_SHORT; r = sc_transmit_apdu(card, &apdu); if (r) { fprintf(stderr, "APDU transmit failed: %s\n", sc_strerror(r)); return 1; } if (check_apdu(&apdu)) return 1; if (apdu.resplen < 0x04) { printf("expected 4-6 bytes form GET DATA for startkey data, but got only %"SC_FORMAT_LEN_SIZE_T"u\n", apdu.resplen); printf("aborting\n"); return 1; } if (apdu.resp[2] != 0xff) { printf("startkey version is 0x%02x, currently we support only 0xff\n", (int) apdu.resp[2]); printf("aborting\n"); return 1; } if (apdu.resp[3] < 5) { printf("startkey has only %d tries left. to be safe: aborting\n", apdu.resp[3]); return 1; } /* first run GET DATA for lifecycle 00 CA 01 83 * returns 34 MANUFACTURING 20 ADMINISTRATION 10 OPERATIONAL * 26 INITIALIZATION, 23 PERSONALIZATION 3f DEATH * 29 ERASE IN PROGRESS * */ memset(&apdu, 0, sizeof(apdu)); apdu.cla = 0x00; apdu.ins = 0xca; apdu.p1 = 0x01; apdu.p2 = 0x83; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.lc = 0; apdu.le = 256; apdu.cse = SC_APDU_CASE_2_SHORT; r = sc_transmit_apdu(card, &apdu); if (r) { fprintf(stderr, "APDU transmit failed: %s\n", sc_strerror(r)); return 1; } if (check_apdu(&apdu)) return 1; if (apdu.resp[0] == 0x34) { printf("card in manufacturing state\n"); goto erase_state; /* we can leave manufacturing mode with FORMAT, * but before we do that, we need to change the secret * siemens start key to the default 0xff start key. * we know the APDU for that, but it is secret and * siemens so far didn't allow us to publish it. */ } if (apdu.resp[0] != 0x10 && apdu.resp[0] != 0x20) { printf("card is in unknown state 0x%02x, aborting\n", (int) apdu.resp[0]); return 1; /* we should handle ERASE IN PROGRESS (29) too */ } if (apdu.resp[0] == 0x20) { printf("card in administrative state, ok\n"); goto admin_state; } printf("card in operational state, need to switch to admin state\n"); /* PHASE CONTROL 80 10 00 00 */ memset(&apdu, 0, sizeof(apdu)); apdu.cla = 0x80; apdu.ins = 0x10; apdu.p1 = 0x00; apdu.p2 = 0x00; apdu.resp = 00; apdu.lc = 0; apdu.le = 00; apdu.cse = SC_APDU_CASE_1; r = sc_transmit_apdu(card, &apdu); if (r) { fprintf(stderr, "APDU transmit failed: %s\n", sc_strerror(r)); return 1; } if (check_apdu(&apdu)) return 1; /* use GET DATA for lifecycle once more */ memset(&apdu, 0, sizeof(apdu)); apdu.cla = 0x00; apdu.ins = 0xca; apdu.p1 = 0x01; apdu.p2 = 0x83; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.lc = 0; apdu.le = 256; apdu.cse = SC_APDU_CASE_2_SHORT; r = sc_transmit_apdu(card, &apdu); if (r) { fprintf(stderr, "APDU transmit failed: %s\n", sc_strerror(r)); return 1; } if (check_apdu(&apdu)) return 1; if (apdu.resp[0] != 0x20) { printf("card not in administrative state, failed\n"); printf("aborting\n"); return 1; } admin_state: /* use GET DATA for packages - 00 ca 01 88 * returns e1 LEN MM 04 ID ID ID ID 8f 01 SS * MM = Manufacturing ID (01 .. 3f = Siemens * ID ID ID ID = Id of the package * SS = License state (01 enabled, 00 disabled */ memset(&apdu, 0, sizeof(apdu)); apdu.cla = 0x00; apdu.ins = 0xca; apdu.p1 = 0x01; apdu.p2 = 0x88; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.lc = 0; apdu.le = 256; apdu.cse = SC_APDU_CASE_2_SHORT; r = sc_transmit_apdu(card, &apdu); if (r) { fprintf(stderr, "APDU transmit failed: %s\n", sc_strerror(r)); return 1; } if (check_apdu(&apdu)) return 1; if (apdu.resplen != 0x00) { printf("card has packages installed.\n"); printf("you would loose those, and we can't re-install them.\n"); printf("to protect you card: aborting\n"); return 1; } /* now we need to erase the card. Our command is: * ERASE FILES 84 06 00 00 * but it needs to be send using SM 4h mode (signed and enc.) */ { unsigned const char erase_apdu[] = { 0x84, 0x06, 0x00, 0x00 }; if (! cardos_sm4h(erase_apdu, sizeof(erase_apdu), rbuf, sizeof(rbuf), startkey, sizeof(startkey))) return 1; if (verbose) { printf ("Erasing EEPROM!\n"); } memset(&apdu, 0, sizeof(apdu)); apdu.cse = SC_APDU_CASE_3_SHORT; apdu.cla = rbuf[0]; apdu.ins = rbuf[1]; apdu.p1 = rbuf[2]; apdu.p2 = rbuf[3]; apdu.lc = rbuf[4]; apdu.data = &rbuf[5]; apdu.datalen = rbuf[4]; r = sc_transmit_apdu(card, &apdu); if (r) { fprintf(stderr, "APDU transmit failed: %s\n", sc_strerror(r)); return 1; } if (check_apdu(&apdu)) return 1; } erase_state: /* next we need to format the card. Our command is: * FORMAT 84 40 00 01 * with P2 = 01 (go to admin mode after format) * and with data: T L V with tag 62 and value: more TLV * 81 02 00 80 Main Folder size 0x0080 * 85 01 01 no death bit, no deactivation bit, * but checksums bit * 86 0a 00 ... 10 bytes AC with all set to allow (00) * not included: CB tag with secure mode definition * (defaults are fine for us) * * this APDU needs to be send using SM 4h mode (signed and enc.) */ { unsigned const char format_apdu[] = { 0x84, 0x40, 0x00, 0x01, 0x15, 0x62, 0x13, 0x81, 0x02, 0x00, 0x80, 0x85, 0x01, 0x01, 0x86, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; if (verbose) { printf ("Formatting!\n"); } if (! cardos_sm4h(format_apdu, sizeof(format_apdu), rbuf, sizeof(rbuf), startkey, sizeof(startkey))) return 1; memset(&apdu, 0, sizeof(apdu)); apdu.cse = SC_APDU_CASE_3_SHORT; apdu.cla = rbuf[0]; apdu.ins = rbuf[1]; apdu.p1 = rbuf[2]; apdu.p2 = rbuf[3]; apdu.lc = rbuf[4]; apdu.data = &rbuf[5]; apdu.datalen = rbuf[4]; r = sc_transmit_apdu(card, &apdu); if (r) { fprintf(stderr, "APDU transmit failed: %s\n", sc_strerror(r)); return 1; } if (check_apdu(&apdu)) return 1; } return 0; } # else /* ENABLE_OPENSSL */ static int cardos_format(const char *opt_startkey) { printf("Formatting CardOS cards requires OpenSC built with OpenSSL.\n"); printf("Aborting\n"); return 1; } #endif /* ENABLE_OPENSSL */ #ifdef ENABLE_OPENSSL static int cardos_change_startkey(const char *change_startkey_apdu) { #define MAX_APDU 60 unsigned char cardos_version[2]; unsigned char apdu_bin[MAX_APDU]; size_t apdu_len=MAX_APDU; unsigned char checksum[SHA_DIGEST_LENGTH]; static const unsigned char cardos_43b_checksum[SHA_DIGEST_LENGTH] = { 0x5C, 0xD6, 0x8C, 0x2C, 0x24, 0x77, 0x3C, 0xDC, 0x93, 0x73, 0xD8, 0x4B, 0x47, 0x29, 0x19, 0x70, 0x9F, 0xA2, 0x42, 0xB4 }; sc_apdu_t 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, strlen(change_startkey_apdu), -1); } /* use GET DATA for version - 00 ca 01 82 * returns e.g. c8 09 for 4.2B */ memset(&apdu, 0, sizeof(apdu)); apdu.cla = 0x00; apdu.ins = 0xca; apdu.p1 = 0x01; apdu.p2 = 0x82; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.lc = 0; apdu.le = 256; apdu.cse = SC_APDU_CASE_2_SHORT; r = sc_transmit_apdu(card, &apdu); if (r) { fprintf(stderr, "APDU transmit failed: %s\n", sc_strerror(r)); return 1; } if (check_apdu(&apdu)) return 1; if (apdu.resplen != 0x02) { printf("did not receive version info, aborting\n"); return 1; } /* check all supported versions here. need a checksum check for each of them below */ if ( (rbuf[0] != 0xc8 || rbuf[1] != 0x08) ) { /* M4.3B */ printf("currently only CardOS M4.01, M4.2B, M4.2C and M4.3B are supported, aborting\n"); return 1; } cardos_version[0] = rbuf[0]; cardos_version[1] = rbuf[1]; /* GET DATA for startkey index - 00 ca 01 96 * returns 6 bytes PackageLoadKey.Version, PackageLoadKey.Retry * Startkey.Version, Startkey.Retry, 2 internal data byes */ memset(&apdu, 0, sizeof(apdu)); apdu.cla = 0x00; apdu.ins = 0xca; apdu.p1 = 0x01; apdu.p2 = 0x96; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.lc = 0; apdu.le = 256; apdu.cse = SC_APDU_CASE_2_SHORT; r = sc_transmit_apdu(card, &apdu); if (r) { fprintf(stderr, "APDU transmit failed: %s\n", sc_strerror(r)); return 1; } if (check_apdu(&apdu)) return 1; if (apdu.resplen < 0x04) { printf("expected 4-6 bytes form GET DATA for startkey data, but got only %"SC_FORMAT_LEN_SIZE_T"u\n", apdu.resplen); printf("aborting\n"); return 1; } if (apdu.resp[2] != 0x00) { printf("startkey version is 0x%02x, currently we support only 0x00\n", (int) apdu.resp[3]); printf("aborting\n"); return 1; } if (apdu.resp[3] < 5) { printf("startkey has only %d tries left. to be safe: aborting\n", apdu.resp[3]); return 1; } /* now check if the correct APDU was passed */ if (sc_hex_to_bin(change_startkey_apdu, apdu_bin, &apdu_len) != 0) { printf("can't convert startkey apdu to binary format: aborting\n"); return 1; } SHA1(apdu_bin, apdu_len, checksum); if (cardos_version[0] == 0xc8 && cardos_version[1] == 0x08) { if (memcmp(checksum, cardos_43b_checksum, SHA_DIGEST_LENGTH) != 0) { printf("change startkey apdu is wrong, checksum doesn't match\n"); util_hex_dump_asc(stdout, checksum, SHA_DIGEST_LENGTH, -1); util_hex_dump_asc(stdout, cardos_43b_checksum, SHA_DIGEST_LENGTH, -1); printf("aborting\n"); return 1; } goto change_startkey; } printf("checksum for your card not yet implemented, aborting\n"); return 1; change_startkey: /* run change startkey apdu */ memset(&apdu, 0, sizeof(apdu)); apdu.cla = apdu_bin[0]; apdu.ins = apdu_bin[1]; apdu.p1 = apdu_bin[2]; apdu.p2 = apdu_bin[3]; apdu.lc = apdu_bin[4]; apdu.data = &apdu_bin[5]; apdu.datalen = apdu.lc; apdu.resp = 00; apdu.le = 00; apdu.cse = SC_APDU_CASE_3_SHORT; r = sc_transmit_apdu(card, &apdu); if (r) { fprintf(stderr, "APDU transmit failed: %s\n", sc_strerror(r)); return 1; } if (check_apdu(&apdu)) return 1; printf("change startkey command issued with success\n"); /* GET DATA for startkey index - 00 ca 01 96 * returns 6 bytes PackageLoadKey.Version, PackageLoadKey.Retry * Startkey.Version, Startkey.Retry, 2 internal data byes */ memset(&apdu, 0, sizeof(apdu)); apdu.cla = 0x00; apdu.ins = 0xca; apdu.p1 = 0x01; apdu.p2 = 0x96; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.lc = 0; apdu.le = 256; apdu.cse = SC_APDU_CASE_2_SHORT; r = sc_transmit_apdu(card, &apdu); if (r) { fprintf(stderr, "APDU transmit failed: %s\n", sc_strerror(r)); return 1; } if (check_apdu(&apdu)) return 1; if (apdu.resplen < 0x04) { printf("expected 4-6 bytes form GET DATA for startkey data, but got only %"SC_FORMAT_LEN_SIZE_T"u\n", apdu.resplen); printf("aborting\n"); return 1; } if (apdu.resp[2] != 0xff) { printf("startkey version is 0x%02x, should have been changed to 0xff.\n", apdu.resp[2]); printf("aborting\n"); return 1; } printf("startkey is now 0xff, success!\n"); return 0; } # else /* ENABLE_OPENSSL */ static int cardos_change_startkey(const char *change_startkey_apdu) { fprintf(stderr, "Changing the startkey requires OpenSC built with OpenSSL.\n"); fprintf(stderr, "Aborting\n"); return 1; } #endif int main(int argc, char *argv[]) { 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_startkey = NULL; const char *opt_change_startkey = NULL; sc_context_param_t ctx_param; 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"); util_print_usage_and_die(app_name, options, option_help, NULL); case 'i': do_info = 1; action_count++; break; case 'f': do_format = 1; action_count++; break; case 's': opt_startkey = optarg; break; case 'S': do_change_startkey = 1; opt_change_startkey = optarg; action_count++; break; case 'r': opt_reader = optarg; break; case 'v': verbose++; 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", sc_strerror(r)); return 1; } /* 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); 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; } action_count--; } if (do_change_startkey) { if ((err = cardos_change_startkey(opt_change_startkey))) { goto end; } action_count--; } if (do_format) { if ((err = cardos_format(opt_startkey))) { goto end; } action_count--; } end: if (card) { sc_unlock(card); sc_disconnect_card(card); } sc_release_context(ctx); return err; } OpenSC-0.26.1/src/tools/cryptoflex-tool.c000066400000000000000000000726721474147347300202300ustar00rootroot00000000000000/* * cryptoflex-tool.c: Tool for doing various Cryptoflex related stuff * * 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., 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" #include "common/compat_strlcat.h" #include "util.h" static const char *app_name = "cryptoflex-tool"; static char * opt_reader = NULL; static int opt_wait = 0; static int opt_key_num = 1, opt_pin_num = -1; static int verbose = 0; static int opt_exponent = 3; static int opt_mod_length = 1024; static int opt_key_count = 1; static int opt_pin_attempts = 10; static int opt_puk_attempts = 10; static const char *opt_appdf = NULL, *opt_prkeyf = NULL, *opt_pubkeyf = NULL; static u8 *pincode = NULL; static const struct option options[] = { { "list-keys", 0, NULL, 'l' }, { "create-key-files", 1, NULL, 'c' }, { "create-pin-file", 1, NULL, 'P' }, { "generate-key", 0, NULL, 'g' }, { "read-key", 0, NULL, 'R' }, { "verify-pin", 0, NULL, 'V' }, { "key-num", 1, NULL, 'k' }, { "app-df", 1, NULL, 'a' }, { "prkey-file", 1, NULL, 'p' }, { "pubkey-file", 1, NULL, 'u' }, { "exponent", 1, NULL, 'e' }, { "modulus-length", 1, NULL, 'm' }, { "reader", 1, NULL, 'r' }, { "wait", 0, NULL, 'w' }, { "verbose", 0, NULL, 'v' }, { NULL, 0, NULL, 0 } }; static const char *option_help[] = { "Lists all keys in a public key file", "Creates new RSA key files for keys", "Creates a new CHV file", "Generates a new RSA key pair", "Reads a public key from the card", "Verifies CHV1 before issuing commands", "Selects which key number to operate on [1]", "Selects the DF to operate in", "Private key file", "Public key file", "The RSA exponent to use in key generation [3]", "Modulus length to use in key generation [1024]", "Uses reader ", "Wait for card insertion", "Verbose operation, may be used several times", }; static sc_context_t *ctx = NULL; static sc_card_t *card = NULL; static char *getpin(const char *prompt) { char *buf, pass[20]; int i; printf("%s", prompt); fflush(stdout); if (fgets(pass, 20, stdin) == NULL) return NULL; for (i = 0; i < 20; i++) if (pass[i] == '\n') pass[i] = 0; if (strlen(pass) == 0) return NULL; buf = malloc(8); if (buf == NULL) return NULL; if (strlen(pass) > 8) { fprintf(stderr, "PIN code too long.\n"); free(buf); return NULL; } memset(buf, 0, 8); strlcpy(buf, pass, 8); return buf; } static int verify_pin(int pin) { char prompt[50]; int r, tries_left = -1; if (pincode == NULL) { sprintf(prompt, "Please enter CHV%d: ", pin); pincode = (u8 *) getpin(prompt); if (pincode == NULL || strlen((char *) pincode) == 0) return -1; } if (pin != 1 && pin != 2) return -3; r = sc_verify(card, SC_AC_CHV, pin, pincode, 8, &tries_left); if (r) { memset(pincode, 0, 8); free(pincode); pincode = NULL; fprintf(stderr, "PIN code verification failed: %s\n", sc_strerror(r)); return -1; } return 0; } static int select_app_df(void) { sc_path_t path; sc_file_t *file; char str[80]; int r; strcpy(str, "3F00"); if (opt_appdf != NULL) strlcat(str, opt_appdf, sizeof str); sc_format_path(str, &path); r = sc_select_file(card, &path, &file); if (r) { fprintf(stderr, "Unable to select application DF: %s\n", sc_strerror(r)); return -1; } if (file->type != SC_FILE_TYPE_DF) { fprintf(stderr, "Selected application DF is not a DF.\n"); return -1; } sc_file_free(file); if (opt_pin_num >= 0) return verify_pin(opt_pin_num); else return 0; } static void invert_buf(u8 *dest, const u8 *src, size_t c) { size_t i; for (i = 0; i < c; i++) dest[i] = src[c-1-i]; } static BIGNUM * cf2bn(const u8 *buf, size_t bufsize, BIGNUM *num) { u8 tmp[512]; invert_buf(tmp, buf, bufsize); return BN_bin2bn(tmp, (int)bufsize, num); } static int bn2cf(const BIGNUM *num, u8 *buf) { u8 tmp[512]; int r; r = BN_bn2bin(num, tmp); if (r <= 0) return r; invert_buf(buf, tmp, r); return r; } static int parse_public_key(const u8 *key, size_t keysize, EVP_PKEY *pkey) { const u8 *p = key; BIGNUM *n, *e; 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"); return -1; } p += 3; n = BN_new(); if (n == NULL) { sc_log_openssl(ctx); return -1; } cf2bn(p, 2 * base, n); p += 2 * base; p += base; p += 2 * base; e = BN_new(); if (e == NULL) return -1; cf2bn(p, 4, e); #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(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; bnctx = BN_CTX_new(); if (bnctx == NULL) return -1; BN_CTX_start(bnctx); r0 = BN_CTX_get(bnctx); r1 = BN_CTX_get(bnctx); r2 = BN_CTX_get(bnctx); 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) { sc_log_openssl(ctx); fprintf(stderr, "BN_mod_inverse() failed.\n"); return -1; } BN_CTX_end(bnctx); BN_CTX_free(bnctx); return 0; } 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; 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) { fprintf(stderr, "Invalid private key.\n"); return -1; } p += 3; bn_p = BN_new(); if (bn_p == NULL) { sc_log_openssl(ctx); return -1; } cf2bn(p, base, bn_p); p += base; q = BN_new(); if (q == NULL) { sc_log_openssl(ctx); return -1; } cf2bn(p, base, q); p += base; iqmp = BN_new(); if (iqmp == NULL) { sc_log_openssl(ctx); return -1; } cf2bn(p, base, iqmp); p += base; dmp1 = BN_new(); if (dmp1 == NULL) { sc_log_openssl(ctx); return -1; } cf2bn(p, base, dmp1); p += base; dmq1 = BN_new(); if (dmq1 == NULL) { sc_log_openssl(ctx); return -1; } cf2bn(p, base, dmq1); #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; } /* 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; } #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; } /* 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(EVP_PKEY *pkey) { int r; sc_path_t path; sc_file_t *file; u8 buf[2048], *p = buf; size_t bufsize, keysize; r = select_app_df(); if (r) return 1; sc_format_path("I1012", &path); r = sc_select_file(card, &path, &file); if (r) { fprintf(stderr, "Unable to select public key file: %s\n", sc_strerror(r)); return 2; } bufsize = MIN(file->size, sizeof buf); sc_file_free(file); r = sc_read_binary(card, 0, buf, bufsize, 0); if (r < 0) { fprintf(stderr, "Unable to read public key file: %s\n", sc_strerror(r)); return 2; } bufsize = r; do { if (bufsize < 4) return 3; keysize = (p[0] << 8) | p[1]; if (keysize == 0) break; if (keysize < 3) return 3; if (p[2] == opt_key_num) break; p += keysize; bufsize -= keysize; } while (1); if (keysize == 0) { printf("Key number %d not found.\n", opt_key_num); return 2; } return parse_public_key(p, keysize, pkey); } static int read_private_key(EVP_PKEY *pkey) { int r; sc_path_t path; sc_file_t *file; const sc_acl_entry_t *e; u8 buf[2048], *p = buf; size_t bufsize, keysize; r = select_app_df(); if (r) return 1; sc_format_path("I0012", &path); r = sc_select_file(card, &path, &file); if (r) { fprintf(stderr, "Unable to select private key file: %s\n", sc_strerror(r)); return 2; } e = sc_file_get_acl_entry(file, SC_AC_OP_READ); if (e == NULL || e->method == SC_AC_NEVER) return 10; bufsize = MIN(file->size, sizeof buf); sc_file_free(file); r = sc_read_binary(card, 0, buf, bufsize, 0); if (r < 0) { fprintf(stderr, "Unable to read private key file: %s\n", sc_strerror(r)); return 2; } bufsize = r; do { if (bufsize < 4) return 3; keysize = (p[0] << 8) | p[1]; if (keysize == 0) break; if (keysize < 3) return 3; if (p[2] == opt_key_num) break; p += keysize; bufsize -= keysize; } while (1); if (keysize == 0) { printf("Key number %d not found.\n", opt_key_num); return 2; } return parse_private_key(p, keysize, pkey); } static int read_key(void) { EVP_PKEY *pkey = NULL; u8 buf[1024], *p = buf; u8 b64buf[2048]; int r; r = read_public_key(pkey); if (r) return r; r = i2d_PUBKEY(pkey, &p); if (r <= 0) { sc_log_openssl(ctx); fprintf(stderr, "Error encoding public key.\n"); return -1; } r = sc_base64_encode(buf, r, b64buf, sizeof(b64buf), 64); if (r < 0) { fprintf(stderr, "Error in Base64 encoding: %s\n", sc_strerror(r)); return -1; } printf("-----BEGIN PUBLIC KEY-----\n%s-----END PUBLIC KEY-----\n", b64buf); r = read_private_key(pkey); if (r == 10) return 0; else if (r) return r; p = buf; r = i2d_PrivateKey(pkey, &p); if (r <= 0) { sc_log_openssl(ctx); fprintf(stderr, "Error encoding private key.\n"); return -1; } r = sc_base64_encode(buf, r, b64buf, sizeof(b64buf), 64); if (r < 0) { fprintf(stderr, "Error in Base64 encoding: %s\n", sc_strerror(r)); return -1; } printf("-----BEGIN RSA PRIVATE KEY-----\n%s-----END RSA PRIVATE KEY-----\n", b64buf); return 0; } static int list_keys(void) { int r, idx = 0; sc_path_t path; u8 buf[2048], *p = buf; size_t keysize, i; int mod_lens[] = { 512, 768, 1024, 2048 }; size_t sizes[] = { 167, 247, 327, 647 }; r = select_app_df(); if (r) return 1; sc_format_path("I1012", &path); r = sc_select_file(card, &path, NULL); if (r) { fprintf(stderr, "Unable to select public key file: %s\n", sc_strerror(r)); return 2; } do { int mod_len = -1; r = sc_read_binary(card, idx, buf, 3, 0); if (r < 0) { fprintf(stderr, "Unable to read public key file: %s\n", sc_strerror(r)); return 2; } keysize = (p[0] << 8) | p[1]; if (keysize == 0) break; idx += keysize; for (i = 0; i < sizeof(sizes)/sizeof(sizes[ 0]); i++) if (sizes[i] == keysize) mod_len = mod_lens[i]; if (mod_len < 0) printf("Key %d -- unknown modulus length\n", p[2] & 0x0F); else printf("Key %d -- Modulus length %d\n", p[2] & 0x0F, mod_len); } while (1); return 0; } static int generate_key(void) { sc_apdu_t apdu; u8 sbuf[4]; u8 p2; int r; switch (opt_mod_length) { case 512: p2 = 0x40; break; case 768: p2 = 0x60; break; case 1024: p2 = 0x80; break; case 2048: p2 = 0x00; break; default: fprintf(stderr, "Invalid modulus length.\n"); return 2; } sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x46, (u8) opt_key_num-1, p2); apdu.cla = 0xF0; apdu.lc = 4; apdu.datalen = 4; apdu.data = sbuf; sbuf[0] = opt_exponent & 0xFF; sbuf[1] = (opt_exponent >> 8) & 0xFF; sbuf[2] = (opt_exponent >> 16) & 0xFF; sbuf[3] = (opt_exponent >> 24) & 0xFF; r = select_app_df(); if (r) return 1; if (verbose) printf("Generating key...\n"); r = sc_transmit_apdu(card, &apdu); if (r) { fprintf(stderr, "APDU transmit failed: %s\n", sc_strerror(r)); if (r == SC_ERROR_TRANSMIT_FAILED) fprintf(stderr, "Reader has timed out. It is still possible that the key generation has\n" "succeeded.\n"); return 1; } if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { printf("Key generation successful.\n"); return 0; } if (apdu.sw1 == 0x69 && apdu.sw2 == 0x82) fprintf(stderr, "CHV1 not verified or invalid exponent value.\n"); else fprintf(stderr, "Card returned SW1=%02X, SW2=%02X.\n", apdu.sw1, apdu.sw2); return 1; } static int create_key_files(void) { sc_file_t *file; int mod_lens[] = { 512, 768, 1024, 2048 }; int sizes[] = { 163, 243, 323, 643 }; int size = -1; int r; size_t i; for (i = 0; i < sizeof(mod_lens) / sizeof(int); i++) if (mod_lens[i] == opt_mod_length) { size = sizes[i]; break; } if (size == -1) { fprintf(stderr, "Invalid modulus length.\n"); return 1; } if (verbose) printf("Creating key files for %d keys.\n", opt_key_count); file = sc_file_new(); if (!file) { fprintf(stderr, "out of memory.\n"); return 1; } file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = SC_FILE_EF_TRANSPARENT; file->id = 0x0012; file->size = opt_key_count * size + 3; sc_file_add_acl_entry(file, SC_AC_OP_READ, SC_AC_NEVER, SC_AC_KEY_REF_NONE); sc_file_add_acl_entry(file, SC_AC_OP_UPDATE, SC_AC_CHV, 1); sc_file_add_acl_entry(file, SC_AC_OP_INVALIDATE, SC_AC_CHV, 1); sc_file_add_acl_entry(file, SC_AC_OP_REHABILITATE, SC_AC_CHV, 1); if (select_app_df()) { sc_file_free(file); return 1; } r = sc_create_file(card, file); sc_file_free(file); if (r) { fprintf(stderr, "Unable to create private key file: %s\n", sc_strerror(r)); return 1; } file = sc_file_new(); if (!file) { fprintf(stderr, "out of memory.\n"); return 1; } file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = SC_FILE_EF_TRANSPARENT; file->id = 0x1012; file->size = opt_key_count * (size + 4) + 3; sc_file_add_acl_entry(file, SC_AC_OP_READ, SC_AC_NONE, SC_AC_KEY_REF_NONE); sc_file_add_acl_entry(file, SC_AC_OP_UPDATE, SC_AC_CHV, 1); sc_file_add_acl_entry(file, SC_AC_OP_INVALIDATE, SC_AC_CHV, 1); sc_file_add_acl_entry(file, SC_AC_OP_REHABILITATE, SC_AC_CHV, 1); if (select_app_df()) { sc_file_free(file); return 1; } r = sc_create_file(card, file); sc_file_free(file); if (r) { fprintf(stderr, "Unable to create public key file: %s\n", sc_strerror(r)); return 1; } if (verbose) printf("Key files generated successfully.\n"); return 0; } static int read_rsa_privkey(EVP_PKEY **pkey_out) { 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); *pkey_out = pkey; return 0; } 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 (EVP_PKEY_bits(pkey)) { case 512: base = 32; break; case 768: base = 48; break; case 1024: base = 64; break; case 2048: base = 128; break; } if (base == 0) { fprintf(stderr, "Key length invalid.\n"); return 2; } *p++ = (5 * base + 3) >> 8; *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"); rv = 2; goto end; } memcpy(p, bnbuf, base); p += base; r = bn2cf(rsa_q, bnbuf); if (r != base) { fprintf(stderr, "Invalid private key.\n"); 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"); rv = 2; goto end; } memcpy(p, bnbuf, base); p += base; r = bn2cf(rsa_dmp1, bnbuf); if (r != base) { fprintf(stderr, "Invalid private key.\n"); rv = 2; goto end; } memcpy(p, bnbuf, base); p += base; r = bn2cf(rsa_dmq1, bnbuf); if (r != base) { fprintf(stderr, "Invalid private key.\n"); rv = 2; goto end; } memcpy(p, bnbuf, base); p += base; memcpy(key, buf, p - buf); *keysize = p - buf; 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(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 (EVP_PKEY_bits(pkey)) { case 512: base = 32; break; case 768: base = 48; break; case 1024: base = 64; break; case 2048: base = 128; break; } if (base == 0) { fprintf(stderr, "Key length invalid.\n"); return 2; } *p++ = (5 * base + 7) >> 8; *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"); rv = 2; goto end; } memcpy(p, bnbuf, 2*base); p += 2*base; memset(p, 0, base); p += base; memset(bnbuf, 0, 2*base); 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; 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) { int r, idx = 0; sc_path_t path; r = select_app_df(); if (r) return 1; sc_format_path("I1012", &path); r = sc_select_file(card, &path, NULL); if (r) { fprintf(stderr, "Unable to select public key file: %s\n", sc_strerror(r)); return 2; } 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)); return 2; } return 0; } static int update_private_key(const u8 *key, size_t keysize) { int r, idx = 0; sc_path_t path; r = select_app_df(); if (r) return 1; sc_format_path("I0012", &path); r = sc_select_file(card, &path, NULL); if (r) { fprintf(stderr, "Unable to select private key file: %s\n", sc_strerror(r)); return 2; } 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)); return 2; } return 0; } static int store_key(void) { u8 prv[1024], pub[1024]; size_t prvsize = 0, pubsize = 0; int r; EVP_PKEY *pkey = NULL; r = read_rsa_privkey(&pkey); if (r) return r; r = encode_private_key(pkey, prv, &prvsize); if (r) return r; r = encode_public_key(pkey, pub, &pubsize); if (r) return r; if (verbose) printf("Storing private key...\n"); r = select_app_df(); if (r) return r; r = update_private_key(prv, prvsize); if (r) return r; if (verbose) printf("Storing public key...\n"); r = select_app_df(); if (r) return r; r = update_public_key(pub, pubsize); if (r) return r; return 0; } static int create_pin_file(const sc_path_t *inpath, int chv, const char *key_id) { char prompt[40], *pin, *puk; char buf[30], *p = buf; sc_path_t file_id, path; sc_file_t *file; size_t len; int r; file_id = *inpath; if (file_id.len < 2) return -1; if (chv == 1) sc_format_path("I0000", &file_id); else if (chv == 2) sc_format_path("I0100", &file_id); else return -1; r = sc_select_file(card, inpath, NULL); if (r) return -1; r = sc_select_file(card, &file_id, NULL); if (r == 0) return 0; sprintf(prompt, "Please enter CHV%d%s: ", chv, key_id); pin = getpin(prompt); if (pin == NULL) return -1; sprintf(prompt, "Please enter PUK for CHV%d%s: ", chv, key_id); puk = getpin(prompt); if (puk == NULL) { free(pin); return -1; } memset(p, 0xFF, 3); p += 3; memcpy(p, pin, 8); p += 8; *p++ = opt_pin_attempts; *p++ = opt_pin_attempts; memcpy(p, puk, 8); p += 8; *p++ = opt_puk_attempts; *p++ = opt_puk_attempts; len = p - buf; free(pin); free(puk); file = sc_file_new(); file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = SC_FILE_EF_TRANSPARENT; sc_file_add_acl_entry(file, SC_AC_OP_READ, SC_AC_NEVER, SC_AC_KEY_REF_NONE); if (inpath->len == 2 && inpath->value[0] == 0x3F && inpath->value[1] == 0x00) sc_file_add_acl_entry(file, SC_AC_OP_UPDATE, SC_AC_AUT, 1); else sc_file_add_acl_entry(file, SC_AC_OP_UPDATE, SC_AC_CHV, 2); sc_file_add_acl_entry(file, SC_AC_OP_INVALIDATE, SC_AC_AUT, 1); sc_file_add_acl_entry(file, SC_AC_OP_REHABILITATE, SC_AC_AUT, 1); file->size = len; file->id = (file_id.value[0] << 8) | file_id.value[1]; r = sc_create_file(card, file); sc_file_free(file); if (r) { fprintf(stderr, "PIN file creation failed: %s\n", sc_strerror(r)); return r; } path = *inpath; sc_append_path(&path, &file_id); r = sc_select_file(card, &path, NULL); if (r) { fprintf(stderr, "Unable to select created PIN file: %s\n", sc_strerror(r)); return r; } r = sc_update_binary(card, 0, (const u8 *) buf, len, 0); if (r < 0) { fprintf(stderr, "Unable to update created PIN file: %s\n", sc_strerror(r)); return r; } return 0; } static int create_pin(void) { sc_path_t path; char buf[80]; if (opt_pin_num != 1 && opt_pin_num != 2) { fprintf(stderr, "Invalid PIN number. Possible values: 1, 2.\n"); return 2; } strcpy(buf, "3F00"); if (opt_appdf != NULL) strlcat(buf, opt_appdf, sizeof buf); sc_format_path(buf, &path); return create_pin_file(&path, opt_pin_num, ""); } int main(int argc, char *argv[]) { int err = 0, r, c, long_optind = 0; int action_count = 0; int do_read_key = 0; int do_generate_key = 0; int do_create_key_files = 0; int do_list_keys = 0; int do_store_key = 0; int do_create_pin_file = 0; sc_context_param_t ctx_param; while (1) { c = getopt_long(argc, argv, "P:Vslgc:Rk:r:p:u:e:m:vwa:", options, &long_optind); if (c == -1) break; if (c == '?') util_print_usage_and_die(app_name, options, option_help, NULL); switch (c) { case 'l': do_list_keys = 1; action_count++; break; case 'P': do_create_pin_file = 1; opt_pin_num = atoi(optarg); action_count++; break; case 'R': do_read_key = 1; action_count++; break; case 'g': do_generate_key = 1; action_count++; break; case 'c': do_create_key_files = 1; opt_key_count = atoi(optarg); action_count++; break; case 's': do_store_key = 1; action_count++; break; case 'k': opt_key_num = atoi(optarg); if (opt_key_num < 1 || opt_key_num > 15) { fprintf(stderr, "Key number invalid.\n"); exit(2); } break; case 'V': opt_pin_num = 1; break; case 'e': opt_exponent = atoi(optarg); break; case 'm': opt_mod_length = atoi(optarg); break; case 'p': opt_prkeyf = optarg; break; case 'u': opt_pubkeyf = optarg; break; case 'r': opt_reader = optarg; break; case 'v': verbose++; break; case 'w': opt_wait = 1; break; case 'a': opt_appdf = optarg; break; } } if (action_count == 0) util_print_usage_and_die(app_name, options, option_help, NULL); 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", sc_strerror(r)); return 1; } 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) { if ((err = create_pin()) != 0) goto end; action_count--; } if (do_create_key_files) { if ((err = create_key_files()) != 0) goto end; action_count--; } if (do_generate_key) { if ((err = generate_key()) != 0) goto end; action_count--; } if (do_store_key) { if ((err = store_key()) != 0) goto end; action_count--; } if (do_list_keys) { if ((err = list_keys()) != 0) goto end; action_count--; } if (do_read_key) { if ((err = read_key()) != 0) goto end; action_count--; } if (pincode != NULL) { memset(pincode, 0, 8); free(pincode); } end: if (card) { sc_unlock(card); sc_disconnect_card(card); } sc_release_context(ctx); return err; } OpenSC-0.26.1/src/tools/dnie-tool.c000066400000000000000000000140101474147347300167260ustar00rootroot00000000000000/* * dnie-tool.c: DNIe tool * * Copyright (C) 2011 Juan Antonio Martinez * * Based on file rutoken-tool.c from Pavel Mironchik * and Eugene Hermann * * 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 #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include #include "libopensc/opensc.h" #include "libopensc/errors.h" #include "libopensc/cardctl.h" #include "libopensc/cards.h" #include "libopensc/pkcs15.h" #include "util.h" /* win32 needs this in open(2) */ #ifndef O_BINARY #define O_BINARY 0 #endif static const char *app_name = "dnie-tool"; #define OP_NONE 0 /* no operation requested */ #define OP_GET_DATA 1 /* retrieve DNIe number, apellidos, nombre */ #define OP_GET_IDESP 2 /* retrieve IDESP */ #define OP_GET_VERSION 4 /* retrieve DNIe version number */ #define OP_GET_SERIALNR 8 /* Get SerialNumber */ static const struct option options[] = { {"reader", 1, NULL, 'r'}, {"wait", 0, NULL, 'w'}, {"pin", 1, NULL, 'p'}, {"idesp", 0, NULL, 'i'}, {"version", 0, NULL, 'V'}, {"data", 0, NULL, 'd'}, {"serial", 0, NULL, 's'}, {"all", 0, NULL, 'a'}, {"verbose", 0, NULL, 'v'}, {NULL, 0, NULL, 0 } }; static const char *option_help[] = { "Uses reader number [0]", "Wait for a card to be inserted", "Specify PIN", "Retrieve IDESP", "Gets DNIe software version", "Show DNIe number, Name, and SurName", "Show DNIe serial number", "Display all the information available", "Verbose operation, may be used several times", }; /* Get DNIe device extra information */ int main(int argc, char* argv[]) { int opt_wait = 0; const char *opt_pin = NULL; const char *opt_reader = NULL; int opt_operation = OP_NONE; int verbose = 0; int err = 0; sc_context_t *ctx = NULL; sc_context_param_t ctx_param; sc_card_t *card = NULL; int c, r; char *data[] = { NULL, NULL, NULL, NULL, NULL }; sc_serial_number_t serial; while ((c = getopt_long(argc, argv, "r:wp:iVdsav", options, (int *) 0)) != -1) { switch (c) { case 'r': opt_reader = optarg; break; case 'w': opt_wait = 1; break; case 'p': util_get_pin(optarg, &opt_pin); break; case 'i': opt_operation |= OP_GET_IDESP; break; case 'V': opt_operation |= OP_GET_VERSION; break; case 'd': opt_operation |= OP_GET_DATA; break; case 's': opt_operation |= OP_GET_SERIALNR; break; case 'a': opt_operation = OP_GET_IDESP | OP_GET_VERSION | OP_GET_DATA | OP_GET_SERIALNR; break; 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; } /* 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) ) { fprintf(stderr, "Error: Cannot connect with card\n"); err = -1; goto dnie_tool_end; } /* 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); if (r) { fprintf(stderr, "Error: PIN verification failed: %s", sc_strerror(r)); if (r == SC_ERROR_PIN_CODE_INCORRECT) fprintf(stderr, " (tries left %d)", tries_left); putc('\n', stderr); err=-1; goto dnie_tool_end; } } 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: (Not available)\n"); else printf("IDESP: %s\n",data[3]); } 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: if (card) { /* sc_lock and sc_connect_card in util_connect_card */ sc_unlock(card); sc_disconnect_card(card); } sc_release_context(ctx); return err; } OpenSC-0.26.1/src/tools/dtrust-tool.c000066400000000000000000000172761474147347300173550ustar00rootroot00000000000000/* * 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/cards.h" #include "libopensc/errors.h" #include "util.h" static const char *app_name = "dtrust-tool"; // clang-format off static const struct option options[] = { {"reader", 1, NULL, 'r'}, {"wait", 0, NULL, 'w'}, {"pin-status", 0, NULL, 's'}, {"check-transport-protection", 0, NULL, 'c'}, {"unlock-transport-protection", 0, NULL, 'u'}, {"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", "Show PIN status", "Check transport protection", "Unlock transport protection", "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 int opt_status = 0; static int opt_check = 0; static int opt_unlock = 0; void pin_status(sc_card_t *card, int ref, const char *pin_label) { int r; struct sc_pin_cmd_data data; int tries_left = 0; 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, &tries_left); if (r == SC_SUCCESS) { if (tries_left < 0) printf("%s: usable\n", pin_label); else printf("%s: usable (%d tries left)\n", pin_label, 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) printf("%s: not usable (transport protection already broken)\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) { struct sc_apdu apdu; int r; u8 buf[6]; u8 prot_intact[6] = {0xE3, 0x04, 0x90, 0x02, 0x00, 0x01}; u8 prot_broken[6] = {0xE3, 0x04, 0x90, 0x02, 0x00, 0x00}; sc_format_apdu_ex(&apdu, 0x80, 0xCA, 0x00, 0x0B, NULL, 0, buf, sizeof(buf)); r = sc_transmit_apdu(card, &apdu); if (r != SC_SUCCESS) { fprintf(stderr, "Check transport protection: APDU transmit failed (%s)\n", sc_strerror(r)); return -1; } r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r != SC_SUCCESS) { fprintf(stderr, "Check transport protection: GET_DATA failed (%s)\n", sc_strerror(r)); return -1; } if (apdu.resplen == sizeof(prot_intact) && !memcmp(apdu.resp, prot_intact, 6)) { printf("Transport protection is still intact.\n"); return 0; } else if (apdu.resplen == sizeof(prot_broken) && !memcmp(apdu.resp, prot_broken, 6)) { printf("Transport protection is broken.\n"); return 1; } fprintf(stderr, "Check transport protection: illegal response: "); util_hex_dump(stderr, apdu.resp, apdu.resplen, " "); fprintf(stderr, "\n"); return -1; } void unlock_transport_protection(sc_card_t *card) { struct sc_pin_cmd_data data; int r; char *tpin = NULL; char *qespin1 = NULL; char *qespin2 = NULL; size_t tpin_len = 0; size_t qespin1_len = 0; size_t qespin2_len = 0; int tries_left; memset(&data, 0, sizeof(data)); data.cmd = SC_PIN_CMD_CHANGE; data.pin_type = SC_AC_CHV; data.pin_reference = 0x87; 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 { printf("Enter Transport PIN:"); r = util_getpass(&tpin, &tpin_len, stdin); if (r < 0 || tpin == NULL) { fprintf(stderr, "Unable to get PIN"); return; } printf("Enter new Signature PIN:"); r = util_getpass(&qespin1, &qespin1_len, stdin); if (r < 0 || qespin1 == NULL) { fprintf(stderr, "Unable to get PIN"); goto fail; } printf("Enter new Signature PIN again:"); r = util_getpass(&qespin2, &qespin1_len, stdin); if (r < 0 || qespin2 == NULL) { fprintf(stderr, "Unable to get PIN"); goto fail; } if (strcmp(qespin1, qespin2)) { fprintf(stderr, "New signature PINs doesn't match.\n"); goto fail; } data.pin1.data = (u8 *)tpin; data.pin1.len = strlen(tpin); data.pin2.data = (u8 *)qespin1; data.pin2.len = strlen(qespin1); } r = sc_pin_cmd(card, &data, &tries_left); 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", tries_left); else printf("Can't change pin: %s\n", sc_strerror(r)); fail: if (qespin2 != NULL) { sc_mem_clear(qespin2, qespin2_len); free(qespin2); } if (qespin1 != NULL) { sc_mem_clear(qespin1, qespin1_len); free(qespin1); } if (tpin != NULL) { sc_mem_clear(tpin, tpin_len); free(tpin); } } int main(int argc, char *argv[]) { int r, c, long_optind = 0; sc_context_param_t ctx_param; sc_card_t *card = NULL; sc_context_t *ctx = NULL; 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 's': opt_status = 1; break; case 'c': opt_check = 1; break; case 'u': opt_unlock = 1; 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) { printf("Failed to establish context: %s\n", sc_strerror(r)); return 1; } r = sc_set_card_driver(ctx, "dtrust"); if (r) { printf("Driver 'dtrust' not found!\n"); goto out; } r = util_connect_card(ctx, &card, opt_reader, opt_wait); if (r) goto out; /* * We have to select the QES app to verify and change the QES PIN. */ sc_format_path("3F000101", &path); r = sc_select_file(card, &path, NULL); if (r) goto out; if (opt_status) { if (card->type == SC_CARD_TYPE_DTRUST_V4_1_STD || card->type == SC_CARD_TYPE_DTRUST_V4_1_MULTI || card->type == SC_CARD_TYPE_DTRUST_V4_1_M100) pin_status(card, 0x03, "Card Holder PIN"); pin_status(card, 0x04, "Card Holder PUK"); pin_status(card, 0x87, "Signature PIN"); pin_status(card, 0x8B, "Transport PIN"); } if (opt_check) check_transport_protection(card); if (opt_unlock) { r = check_transport_protection(card); if (r) printf("Cannot remove transport protection.\n"); else unlock_transport_protection(card); } out: if (card) { sc_unlock(card); sc_disconnect_card(card); } sc_release_context(ctx); return EXIT_SUCCESS; } OpenSC-0.26.1/src/tools/egk-tool-cmdline.c000066400000000000000000000467161474147347300202100ustar00rootroot00000000000000/* 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 : */ OpenSC-0.26.1/src/tools/egk-tool-cmdline.h000066400000000000000000000201531474147347300202000ustar00rootroot00000000000000/** @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 */ OpenSC-0.26.1/src/tools/egk-tool.c000066400000000000000000000171241474147347300165660ustar00rootroot00000000000000/* * 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 (Z_OK == inflateInit2(&stream, (15 + 32)) && Z_STREAM_END == inflate(&stream, Z_FINISH)) { *uncompressed_len = stream.total_out; } else { return SC_ERROR_INVALID_DATA; } 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; } OpenSC-0.26.1/src/tools/egk-tool.ggo.in000066400000000000000000000015041474147347300175200ustar00rootroot00000000000000package "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 " OpenSC-0.26.1/src/tools/eidenv.c000066400000000000000000000244431474147347300163210ustar00rootroot00000000000000/* * eidenv.c: Belpic utility * * Copyright (C) 2004 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 */ #include "config.h" #include #ifndef _WIN32 #include #else #include #endif #include #include #include #include "libopensc/opensc.h" #include "libopensc/asn1.h" #include "libopensc/cards.h" #include "util.h" static char *opt_reader = NULL; static int stats = 0; static int opt_wait = 0; static char *exec_program = NULL; static int exit_status = EXIT_FAILURE; static const struct option options[] = { {"reader", required_argument, NULL, 'r'}, {"print", no_argument, NULL, 'p'}, {"exec", required_argument, NULL, 'x'}, {"stats", no_argument, NULL, 't'}, {"help", no_argument, NULL, 'h'}, {"wait", no_argument, NULL, 'w'}, {"version", no_argument, NULL, 'V'}, {NULL, 0, NULL, 0} }; /* Probably not used, but needed to build on Windows */ static const char *app_name = "eidenv"; static void show_version(void) { fprintf(stderr, "eidenv - Belpic utility version " PACKAGE_VERSION "\n" "\n" "Copyright (c) 2004 Martin Paljak \n" "Licensed under LGPL v2\n"); } static void show_help(void) { show_version(); fprintf(stderr, "-h --help - show this text and exit\n" "-v --version - show version and exit\n" "-r --reader - the reader to use\n" "-w --wait - wait for a card to be inserted\n" "-p --print - print the datafile\n" "-t --stats - show usage counts of keys\n" "-x --exec - execute a program with data in env vars.\n"); } static void decode_options(int argc, char **argv) { int c; while ((c = getopt_long(argc, argv,"pwtr:x:hV", options, (int *) 0)) != -1) { switch (c) { case 'r': opt_reader = optarg; break; case 't': stats = !stats; break; case 'x': if (exec_program) free(exec_program); exec_program = strdup(optarg); break; case 'h': show_help(); exit(EXIT_SUCCESS); break; case 'p': break; case 'w': opt_wait = 1; break; case 'V': show_version(); exit(EXIT_SUCCESS); break; default: show_help(); exit(EXIT_FAILURE); } } } /* Select and read a transparent EF */ static int read_transp(sc_card_t *card, const char *pathstring, unsigned char *buf, int buflen) { sc_path_t path; int r; sc_format_path(pathstring, &path); r = sc_select_file(card, &path, NULL); if (r < 0) fprintf(stderr, "\nFailed to select file %s: %s\n", pathstring, sc_strerror(r)); else { r = sc_read_binary(card, 0, buf, buflen, 0); if (r < 0) fprintf(stderr, "\nFailed to read %s: %s\n", pathstring, sc_strerror(r)); } return r; } /* 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) { int i; for (i = len - 1; i >= 0; i--) { buf[2 * i + 1] = hextable[((unsigned char) buf[i]) % 16]; buf[2 * i] = hextable[((unsigned char) buf[i]) / 16]; } } static void exportprint(const char *key, const char *val) { if (exec_program) { char * cp; cp = malloc(strlen(key) + strlen(val) + 2); if (cp) { strcpy(cp, key); strcat(cp, "="); strcat(cp, val); putenv(cp); free(cp); } } else printf("%s: %s\n", key, val); } static void do_belpic(sc_card_t *card) { /* Contents of the ID file (3F00\DF01\4031) */ struct { char cardnumber[12 + 1]; char chipnumber[2 * 16 + 1]; char validfrom[10 + 1]; char validtill[10 + 1]; char deliveringmunicipality[50 + 1]; /* UTF8 */ char nationalnumber[12 + 1]; char name[90 + 1]; /* UTF8 */ char firstnames[75 + 1]; /* UTF8 */ char initial[3 + 1]; /* UTF8 */ char nationality[65 + 1]; /* UTF8 */ char birthlocation[60 + 1]; /* UTF8 */ char birthdate[12 + 1]; char sex[1 + 1]; char noblecondition[30 + 1]; /* UTF8 */ 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); struct sc_asn1_entry id[] = { {"cardnumber", SC_ASN1_UTF8STRING, 1, 0, id_data.cardnumber, &cardnumberlen}, {"chipnumber", SC_ASN1_OCTET_STRING, 2, 0, id_data.chipnumber, &chipnumberlen}, {"validfrom", SC_ASN1_UTF8STRING, 3, 0, id_data.validfrom, &validfromlen}, {"validtill", SC_ASN1_UTF8STRING, 4, 0, id_data.validtill, &validtilllen}, {"deliveringmunicipality", SC_ASN1_UTF8STRING, 5, 0, id_data.deliveringmunicipality, &deliveringmunicipalitylen}, {"nationalnumber", SC_ASN1_UTF8STRING, 6, 0, id_data.nationalnumber, &nationalnumberlen}, {"name", SC_ASN1_UTF8STRING, 7, 0, id_data.name, &namelen}, {"firstname(s)", SC_ASN1_UTF8STRING, 8, 0, id_data.firstnames, &firstnameslen}, {"initial", SC_ASN1_UTF8STRING, 9, 0, id_data.initial, &initiallen}, {"nationality", SC_ASN1_UTF8STRING, 10, 0, id_data.nationality, &nationalitylen}, {"birthlocation", SC_ASN1_UTF8STRING, 11, 0, id_data.birthlocation, &birthlocationlen}, {"birthdate", SC_ASN1_UTF8STRING, 12, 0, id_data.birthdate, &birthdatelen}, {"sex", SC_ASN1_UTF8STRING, 13, 0, id_data.sex, &sexlen}, {"noblecondition", SC_ASN1_UTF8STRING, 14, 0, id_data.noblecondition, &nobleconditionlen}, {"documenttype", SC_ASN1_UTF8STRING, 15, 0, id_data.documenttype, &documenttypelen}, {"specialstatus", SC_ASN1_UTF8STRING, 16, 0, id_data.specialstatus, &specialstatuslen}, {NULL, 0, 0, 0, NULL, NULL} }; /* Contents of the Address file (3F00\DF01\4033) */ struct { char streetandnumber[63 + 1]; /* UTF8 */ 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); 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}, {"municipal", SC_ASN1_UTF8STRING, 3, 0, address_data.municipality, &municipalitylen}, {NULL, 0, 0, 0, NULL, NULL}}; unsigned char buff[512]; int r; r = read_transp(card, "3f00df014031", buff, sizeof(buff)); if (r < 0) goto out; memset(&id_data, 0, sizeof(id_data)); r = sc_asn1_decode(card->ctx, id, buff, r, NULL, NULL); if (r < 0) { fprintf(stderr, "\nFailed to decode the ID file: %s\n", sc_strerror(r)); goto out; } exportprint("BELPIC_CARDNUMBER", id_data.cardnumber); bintohex(id_data.chipnumber, chipnumberlen); exportprint("BELPIC_CHIPNUMBER", id_data.chipnumber); exportprint("BELPIC_VALIDFROM", id_data.validfrom); exportprint("BELPIC_VALIDTILL", id_data.validtill); exportprint("BELPIC_DELIVERINGMUNICIPALITY", id_data.deliveringmunicipality); exportprint("BELPIC_NATIONALNUMBER", id_data.nationalnumber); exportprint("BELPIC_NAME", id_data.name); exportprint("BELPIC_FIRSTNAMES", id_data.firstnames); exportprint("BELPIC_INITIAL", id_data.initial); exportprint("BELPIC_NATIONALITY", id_data.nationality); exportprint("BELPIC_BIRTHLOCATION", id_data.birthlocation); exportprint("BELPIC_BIRTHDATE", id_data.birthdate); exportprint("BELPIC_SEX", id_data.sex); exportprint("BELPIC_NOBLECONDITION", id_data.noblecondition); exportprint("BELPIC_DOCUMENTTYPE", id_data.documenttype); exportprint("BELPIC_SPECIALSTATUS", id_data.specialstatus); r = read_transp(card, "3f00df014033", buff, sizeof(buff)); if (r < 0) goto out; memset(&address_data, 0, sizeof(address_data)); r = sc_asn1_decode(card->ctx, address, buff, r, NULL, NULL); if (r < 0) { fprintf(stderr, "\nFailed to decode the Address file: %s\n", sc_strerror(r)); goto out; } exportprint("BELPIC_STREETANDNUMBER", address_data.streetandnumber); exportprint("BELPIC_ZIPCODE", address_data.zipcode); exportprint("BELPIC_MUNICIPALITY", address_data.municipality); out: return; } int main(int argc, char **argv) { sc_context_t *ctx = NULL; sc_context_param_t ctx_param; sc_card_t *card = NULL; int r; /* get options */ decode_options(argc, argv); /* connect to the card */ memset(&ctx_param, 0, sizeof(ctx_param)); ctx_param.ver = 0; ctx_param.app_name = app_name; r = sc_context_create(&ctx, &ctx_param); if (r) { fprintf(stderr, "Failed to establish context: %s\n", sc_strerror(r)); return 1; } 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_BELPIC_EID) do_belpic(card); else { fprintf(stderr, "Not an Belpic card!\n"); goto out; } if (exec_program) { char *const largv[] = {exec_program, NULL}; sc_unlock(card); sc_disconnect_card(card); sc_release_context(ctx); execv(exec_program, largv); /* we should not get here */ perror("execv()"); exit(1); } out: sc_unlock(card); sc_disconnect_card(card); sc_release_context(ctx); exit(exit_status); } OpenSC-0.26.1/src/tools/exe.manifest000066400000000000000000000010041474147347300172000ustar00rootroot00000000000000 OpenSC-0.26.1/src/tools/fread_to_eof.c000066400000000000000000000027171474147347300174630ustar00rootroot00000000000000/* * Copyright (C) 2010 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 #include #include int fread_to_eof(const char *file, unsigned char **buf, size_t *buflen) { FILE *input = NULL; int r = 0; unsigned char *p; if (!buflen || !buf || !file) goto err; #define MAX_READ_LEN 0xfff p = realloc(*buf, MAX_READ_LEN); if (!p) goto err; *buf = p; input = fopen(file, "rb"); if (!input) { goto err; } *buflen = 0; while (feof(input) == 0 && *buflen < MAX_READ_LEN) { *buflen += fread(*buf+*buflen, 1, MAX_READ_LEN-*buflen, input); if (ferror(input)) { goto err; } } r = 1; err: if (input) fclose(input); return r; } OpenSC-0.26.1/src/tools/fread_to_eof.h000066400000000000000000000017131474147347300174630ustar00rootroot00000000000000/* * Copyright (C) 2010 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 _FREAD_TO_EOF_H #define _FREAD_TO_EOF_H #include int fread_to_eof(const char *file, unsigned char **buf, size_t *buflen); #endif OpenSC-0.26.1/src/tools/gids-tool.c000066400000000000000000000400451474147347300167440ustar00rootroot00000000000000/* * gids-tool.c: Support for GIDS smart cards. * * Copyright (C) 2015 Vincent Le Toux (My Smart Logon) * * 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 HAVE_UNISTD_H #include #endif #include #include #include #include #include #include #include "libopensc/sc-ossl-compat.h" #include #include #include #include #include #include "libopensc/opensc.h" #include "libopensc/cardctl.h" #include "libopensc/card-gids.h" #include "libopensc/asn1.h" #include "util.h" static const char *app_name = "gids-tool"; static struct sc_aid gids_aid = { { 0xA0,0x00,0x00,0x03,0x97,0x42,0x54,0x46,0x59 }, 9 }; static int opt_wait = 0; static char *opt_reader = NULL; static int verbose = 0; enum { OPT_SO_PIN = 0x100, OPT_PIN, OPT_SERIAL_NUMBER, OPT_NEW_KEY, }; static const struct option options[] = { { "initialize", 0, NULL, 'X' }, { "admin-key", 1, NULL, OPT_SO_PIN }, { "pin", 1, NULL, OPT_PIN }, { "serial-number", 1, NULL, OPT_SERIAL_NUMBER }, { "unblock", 0, NULL, 'U' }, { "change-admin-key", 0, NULL, 'C' }, { "new-admin-key", 1, NULL, OPT_NEW_KEY }, { "reader", 1, NULL, 'r' }, { "wait", 0, NULL, 'w' }, { "verbose", 0, NULL, 'v' }, { NULL, 0, NULL, 0 } }; static const char *option_help[] = { "Initialize token", "Define the administrator key", "Define user PIN", "Define serial number", "Unblock the user PIN after an administrator authentication", "Change the administrator key", "Define the new administrator key", "Uses reader number [0]", "Wait for a card to be inserted", "Verbose operation, may be used several times", }; 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; size_t len; char *_so_pin = NULL, *_user_pin = NULL, *_serial = NULL; int r; memset(¶m, 0, sizeof(sc_cardctl_gids_init_param_t)); if (so_pin == NULL) { printf("Enter admin key (48 hexadecimal characters) : \n"); printf("Press Enter to set the admin key to 00...00\n"); util_getpass(&_so_pin, NULL, stdin); printf("\n"); } else { _so_pin = (char *)so_pin; } len = sizeof(param.init_code); r = sc_hex_to_bin(_so_pin, param.init_code, &len); if (r < 0) { fprintf(stderr, "Error decoding initialization code (%s)\n", sc_strerror(r)); return -1; } if (len != 24) { fprintf(stderr, "The admin key must be a hexadecimal string of 48 characters\n"); return -1; } if (user_pin == NULL) { printf("Enter initial User-PIN (4 - 16 characters) : "); util_getpass(&_user_pin, NULL, stdin); printf("\n"); } else { _user_pin = (char *)user_pin; } if (serial == NULL) { printf("Enter serial number (32 hexadecimal characters): \n"); printf("Press Enter to set a random serial number\n"); util_getpass(&_serial, NULL, stdin); printf("\n"); } else { _serial = (char *)serial; } if (_serial[0] == '\0') { memset(param.cardid, 0, sizeof(param.cardid)); } else if (strlen(_serial) != 32) { fprintf(stderr, "the serial number must be a hexadecimal string of 32 characters\n"); return -1; } else { len = sizeof(param.cardid); r = sc_hex_to_bin(_serial, param.cardid, &len); if (r < 0) { fprintf(stderr, "Error decoding serial number (%s)\n", sc_strerror(r)); return -1; } } param.user_pin_len = strlen(_user_pin); if (param.user_pin_len < 4) { fprintf(stderr, "PIN must be at least 4 characters long\n"); return -1; } if (param.user_pin_len > 16) { fprintf(stderr, "PIN must not be longer than 16 characters\n"); return -1; } param.user_pin = (u8 *)_user_pin; r = sc_card_ctl(card, SC_CARDCTL_GIDS_INITIALIZE, (void *)¶m); if (r < 0) { fprintf(stderr, "sc_card_ctl(*, SC_CARDCTL_GIDS_INITIALIZE, *) failed with %s\n", sc_strerror(r)); } return 0; } static int unblock(sc_card_t* card, const char *so_pin, const char *user_pin) { int r; char *_so_pin = NULL, *_user_pin = NULL; size_t len; u8 key[24]; struct sc_pin_cmd_data data; memset(&data, 0, sizeof(struct sc_pin_cmd_data)); if (so_pin == NULL) { printf("============================================================\n"); printf("WARNING\n"); printf("Entering an incorrect admin key can break your card\n"); printf("WARNING\n"); printf("============================================================\n"); printf("Enter admin key (48 hexadecimal characters) : "); util_getpass(&_so_pin, NULL, stdin); printf("\n"); } else { _so_pin = (char *)so_pin; } len = sizeof(key); r = sc_hex_to_bin(_so_pin, key, &len); if (r < 0) { fprintf(stderr, "Error decoding initialization code (%s)\n", sc_strerror(r)); return -1; } if (len != 24) { fprintf(stderr, "admin key must be a hexadecimal string of 48 characters\n"); return -1; } r = sc_card_ctl(card, SC_CARDCTL_GIDS_AUTHENTICATE_ADMIN, (void *)key); if (r < 0) { fprintf(stderr, "sc_card_ctl(*, SC_CARDCTL_GIDS_AUTHENTICATE_ADMIN, *) failed with %s\n", sc_strerror(r)); return -1; } printf("Administrator authentication successful\n"); printf("Setting the new PIN\n"); if (user_pin == NULL) { printf("Enter User-PIN (4 - 16 characters) : "); util_getpass(&_user_pin, NULL, stdin); printf("\n"); } else { _user_pin = (char *)user_pin; } data.pin_type = SC_AC_CHV; data.cmd = SC_PIN_CMD_UNBLOCK; data.pin2.len = strlen(_user_pin); data.pin2.data = (unsigned char*) _user_pin; data.pin_reference = 0x80; r = sc_pin_cmd(card, &data, NULL); if (r < 0) { fprintf(stderr, "reset pin failed with %s\n", sc_strerror(r)); return -1; } printf("Unblock PIN done successfully\n"); // the card should have deauthenticated the admin, but to be sure: sc_logout(card); return 0; } static int changeAdminKey(sc_card_t* card, const char *so_pin, const char* new_key) { char *_so_pin = NULL, *_new_key = NULL; size_t len; u8 key[24]; int r; if (so_pin == NULL) { printf("============================================================\n"); printf("WARNING\n"); printf("Entering an incorrect admin key can break your card\n"); printf("WARNING\n"); printf("============================================================\n"); printf("Enter admin key (48 hexadecimal characters) : "); util_getpass(&_so_pin, NULL, stdin); printf("\n"); } else { _so_pin = (char *)so_pin; } len = sizeof(key); r = sc_hex_to_bin(_so_pin, key, &len); if (r < 0) { fprintf(stderr, "Error decoding initialization code (%s)\n", sc_strerror(r)); return -1; } if (len != 24) { fprintf(stderr, "admin key must be a hexadecimal string of 48 characters\n"); return -1; } r = sc_card_ctl(card, SC_CARDCTL_GIDS_AUTHENTICATE_ADMIN, (void *)key); if (r < 0) { fprintf(stderr, "sc_card_ctl(*, SC_CARDCTL_GIDS_AUTHENTICATE_ADMIN, *) failed with %s\n", sc_strerror(r)); return -1; } if (new_key == NULL) { printf("Enter new admin key (48 hexadecimal characters) : "); util_getpass(&_new_key, NULL, stdin); printf("\n"); } else { _new_key = (char *)new_key; } len = sizeof(key); r = sc_hex_to_bin(_new_key, key, &len); if (r < 0) { fprintf(stderr, "Error decoding initialization code (%s)\n", sc_strerror(r)); return -1; } if (len != 24) { fprintf(stderr, "admin key must be a hexadecimal string of 48 characters\n"); return -1; } r = sc_card_ctl(card, SC_CARDCTL_GIDS_SET_ADMIN_KEY, (void *)key); sc_logout(card); if (r < 0) { fprintf(stderr, "sc_card_ctl(*, SC_CARDCTL_GIDS_SET_ADMIN_KEY, *) failed with %s\n", sc_strerror(r)); return -1; } return 0; } // read a DO from the card static int gids_get_DO(sc_card_t* card, int fileIdentifier, int dataObjectIdentifier, u8* response, size_t *responselen) { sc_apdu_t apdu; int r; u8 data[4] = {0x5C, 0x02, (dataObjectIdentifier&0xFF00)>>8, (dataObjectIdentifier&0xFF)}; size_t datasize = 0; const u8* p; u8 buffer[MAX_GIDS_FILE_SIZE]; sc_format_apdu(card, &apdu, response == NULL ? SC_APDU_CASE_3_SHORT : SC_APDU_CASE_4_SHORT, 0xCB, (fileIdentifier&0xFF00)>>8, (fileIdentifier&0xFF)); apdu.lc = 04; apdu.data = data; apdu.datalen = 04; apdu.resp = buffer; apdu.resplen = sizeof(buffer); apdu.le = 256; r = sc_transmit_apdu(card, &apdu); if (r < 0) return r; r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r < 0) return r; p = sc_asn1_find_tag(card->ctx, buffer, sizeof(buffer), dataObjectIdentifier, &datasize); if (!p) { return SC_ERROR_FILE_NOT_FOUND; } if (datasize > *responselen) { return SC_ERROR_BUFFER_TOO_SMALL; } if (response) { memcpy(response, p, datasize); } *responselen = datasize; return SC_SUCCESS; } static int print_info(sc_card_t *card) { int r; u8 buffer[MAX_GIDS_FILE_SIZE]; size_t size = sizeof(buffer); u8 masterfile[MAX_GIDS_FILE_SIZE]; size_t masterfilesize = sizeof(masterfile); u8 cmapfile[MAX_GIDS_FILE_SIZE]; size_t cmapfilesize = sizeof(cmapfile); u8 keymap[MAX_GIDS_FILE_SIZE]; size_t keymapsize = sizeof(keymap); 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"); r = gids_get_DO(card, MF_FI, MF_DO, masterfile, &masterfilesize); if (r < 0) { fprintf(stderr, "unable to retrieve the master file: %s\n", sc_strerror(r)); fprintf(stderr, "Is that a new card ?\n"); return r; } printf("Dumping Files:\n"); if (masterfilesize >= 1) { recordcount = (int) ((masterfilesize-1) / sizeof(gids_mf_record_t)); printf("Found %d entries in the masterfile\n", recordcount); for (i = 0; i < recordcount; i++) { if (records[i].filename[0] == 0) { printf(" Directory: %s\n", records[i].directory); printf(" FileIdentifier: 0x%x\n", records[i].fileIdentifier); printf("\n"); } } for (i = 0; i < recordcount; i++) { if (records[i].filename[0] != 0) { printf(" File: %s\\%s\n", records[i].directory, records[i].filename); printf(" FileIdentifier: 0x%x\n", records[i].fileIdentifier); printf(" DataObjectIdentifier: 0x%x\n", records[i].dataObjectIdentifier); size = sizeof(buffer); r = gids_get_DO(card, records[i].fileIdentifier, records[i].dataObjectIdentifier, buffer, &size); if (r < 0) { printf(" unable to read the file: %s\n", sc_strerror(r)); } else { printf(" Size: %"SC_FORMAT_LEN_SIZE_T"u\n", size); } printf("\n"); if (strcmp(records[i].directory, "mscp") == 0 && strcmp(records[i].filename, "cmapfile") == 0 ) { cmapfilesize = size; memcpy(cmapfile, buffer, size); } } } printf("Dumping containers:\n"); if (cmapfilesize == sizeof(cmapfile)) { printf("Unable to find the container file (mscp\\cmapfile)\n"); } else { PCONTAINER_MAP_RECORD cmaprecords = (PCONTAINER_MAP_RECORD) cmapfile; 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 (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[j].bFlags & CONTAINER_MAP_VALID_CONTAINER) { printf("Valid container"); if (cmaprecords[j].bFlags & CONTAINER_MAP_DEFAULT_CONTAINER) { printf(",Default container"); } } else { printf("Empty container"); } printf("\n"); 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[j].state); printf(" algid: %d\n", keymaprecord[j].algid); printf(" keyref: 0x%x\n", keymaprecord[j].keyref); printf(" key type: "); switch(keymaprecord[j].keytype) { case 0: printf("none\n"); break; case 0x9C: printf("signature\n"); break; case 0x9A: printf("signature + decryption\n"); break; default: printf("unknown\n"); break; } } printf("\n"); } } } } else { printf("No file system found\n"); } return SC_SUCCESS; } int main(int argc, char * argv[]) { int err = 0, r, c, long_optind = 0; int action_count = 0; int do_initialize = 0; int do_unblock = 0; int do_change_admin = 0; sc_path_t path; const char *opt_so_pin = NULL; const char *opt_pin = NULL; const char *opt_serial_number = NULL; const char *opt_new_key = NULL; sc_context_param_t ctx_param; sc_context_t *ctx = NULL; sc_card_t *card = NULL; while (1) { c = getopt_long(argc, argv, "XUCr:wv", options, &long_optind); if (c == -1) break; if (c == '?') util_print_usage_and_die(app_name, options, option_help, NULL); switch (c) { case 'X': do_initialize = 1; action_count++; break; case OPT_SO_PIN: util_get_pin(optarg, &opt_so_pin); break; case OPT_PIN: util_get_pin(optarg, &opt_pin); break; case OPT_SERIAL_NUMBER: util_get_pin(optarg, &opt_serial_number); break; case OPT_NEW_KEY: util_get_pin(optarg, &opt_new_key); break; case 'U': do_unblock = 1; action_count++; break; case 'C': do_change_admin = 1; action_count++; break; case 'r': opt_reader = optarg; break; case 'v': verbose++; break; case 'w': opt_wait = 1; break; } } /* OpenSSL magic */ 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) { fprintf(stderr, "Failed to establish context: %s\n", sc_strerror(r)); exit(1); } 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)); } goto end; } sc_path_set(&path, SC_FILE_TYPE_WORKING_EF, gids_aid.value, gids_aid.len, 0, 0); r = sc_select_file(card, &path, NULL); if (r != SC_SUCCESS) { fprintf(stderr, "Failed to select application: %s\n", sc_strerror(r)); goto fail; } if (do_initialize && initialize(card, opt_so_pin, opt_pin, opt_serial_number)) goto fail; if (do_unblock && unblock(card, opt_so_pin, opt_pin)) goto fail; if (do_change_admin && changeAdminKey(card, opt_so_pin, opt_new_key)) goto fail; if (action_count == 0) { print_info(card); } err = 0; goto end; fail: err = 1; end: if (card) { sc_unlock(card); sc_disconnect_card(card); } sc_release_context(ctx); ERR_print_errors_fp(stderr); return err; } OpenSC-0.26.1/src/tools/goid-tool-cmdline.c000066400000000000000000001620161474147347300203540ustar00rootroot00000000000000/* 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 : */ OpenSC-0.26.1/src/tools/goid-tool-cmdline.h000066400000000000000000000437721474147347300203700ustar00rootroot00000000000000/** @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 */ OpenSC-0.26.1/src/tools/goid-tool.c000066400000000000000000001021371474147347300167410ustar00rootroot00000000000000/* * 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__);*/ OpenSC-0.26.1/src/tools/goid-tool.ggo.in000066400000000000000000000067231474147347300177040ustar00rootroot00000000000000package "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 " OpenSC-0.26.1/src/tools/iasecc-tool.c000066400000000000000000000165051474147347300172510ustar00rootroot00000000000000/* * iasecc-tool.c: Tool for accessing smart cards with libopensc * * Copyright (C) 2001 Juha Yrjölä * Copyright (C) 2011 Viktor TARASOV * * 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 #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include #include "libopensc/opensc.h" #include "libopensc/cardctl.h" #include "libopensc/asn1.h" #include "libopensc/iasecc.h" #include "util.h" static const char *app_name = "iasecc-tool"; static char * opt_bind_to_aid = NULL; static char * opt_reader = NULL; static char * opt_sdo_tag = 0; static int opt_wait = 0; static int verbose = 0; enum { OPT_READER = 0x100, OPT_BIND_TO_AID, OPT_LIST_SDOS, OPT_LIST_APPLICATIONS }; static const struct option options[] = { { "reader", required_argument, NULL, OPT_READER }, { "aid", required_argument, NULL, OPT_BIND_TO_AID }, { "list-applications", no_argument, NULL, OPT_LIST_APPLICATIONS }, { "list-sdos", required_argument, NULL, OPT_LIST_SDOS }, { "wait", no_argument, NULL, 'w' }, { "verbose", no_argument, NULL, 'v' }, { NULL, 0, NULL, 0 } }; static const char *option_help[] = { "Uses reader number ", "Specify AID of the on-card PKCS#15 application to be binded to (in hexadecimal form)", "List the on-card PKCS#15 applications", "List the SDOs with the tag in the current ADF", "Wait for card insertion", "Verbose operation, may be used several times", NULL }; static int list_sdos(char *sdo_tag); static sc_context_t *ctx = NULL; static sc_card_t *card = NULL; static struct sc_pkcs15_card *p15card = NULL; static void _iasecc_print_tlv(char *label, int format_text, struct iasecc_extended_tlv *tlv) { unsigned ii; if (!tlv->value) return; printf("%s:\t", label); for(ii=0; iisize; ii++) { if (format_text) { printf("%c", *(tlv->value + ii)); } else { if (ii) printf(":"); printf("%02X", *(tlv->value + ii)); } } printf("\n"); } static void _iasecc_print_docp(struct iasecc_sdo_docp *docp) { _iasecc_print_tlv("\tname:", 1, &docp->name); _iasecc_print_tlv("\tcontact ACLs", 0, &docp->acls_contact); _iasecc_print_tlv("\tnon repudiation", 0, &docp->non_repudiation); _iasecc_print_tlv("\tsize", 0, &docp->size); _iasecc_print_tlv("\ttries maximum", 0, &docp->tries_maximum); _iasecc_print_tlv("\ttries remaining", 0, &docp->tries_remaining); _iasecc_print_tlv("\tusage maximum", 0, &docp->usage_maximum); _iasecc_print_tlv("\tusage remaining", 0, &docp->usage_remaining); } static void _iasecc_print_crt(struct sc_crt *crt) { printf("\tCRT #%X:\tusage %02X; algo %02X; ref %02X:%02X:...\n", crt->tag, crt->usage, crt->algo, crt->refs[0], crt->refs[1]); } static int list_sdos(char *sdo_tag) { struct iasecc_sdo sdo; struct iasecc_se_info se; 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_l = strtol(sdo_tag + 1, NULL, 16); else if ((strlen(sdo_tag) > 2) && (*(sdo_tag + 1) == 'x' || *(sdo_tag + 1) == 'X')) sdo_class_l = strtol(sdo_tag + 2, NULL, 16); else sdo_class_l = strtol(sdo_tag, NULL, 10); sdo_class = sdo_class_l & 0x7F; if (sdo_class == IASECC_SDO_CLASS_SE) { for (ii=1; ii<0x20; ii++) { memset(&se, 0, sizeof(se)); se.reference = ii; rv = sc_card_ctl(card, SC_CARDCTL_GET_SE_INFO, &se); if (!rv) { printf("Found SE #%X\n", se.reference); _iasecc_print_docp(&se.docp); for(jj=0; jj"); return -1; } static int list_apps(FILE *fout) { unsigned j; int i; for (i=0; i < card->app_count; i++) { struct sc_app_info *info = card->app[i]; fprintf(fout, "Application '%s':\n", info->label); fprintf(fout, "\tAID: "); for(j=0;jaid.len;j++) fprintf(fout, "%02X", info->aid.value[j]); fprintf(fout, "\n"); if (info->ddo.value && info->ddo.len) { fprintf(fout, "\tDDO: "); for(j=0;jddo.len;j++) fprintf(fout, "%02X", info->ddo.value[j]); fprintf(fout, "\n"); } fprintf(fout, "\n"); } return 0; } int main(int argc, char *argv[]) { int err = 0, r, c, long_optind = 0; int do_list_sdos = 0; int do_list_apps = 0; int action_count = 0; sc_context_param_t ctx_param; while (1) { c = getopt_long(argc, argv, "v", options, &long_optind); if (c == -1) break; if (c == '?') util_print_usage_and_die(app_name, options, option_help, NULL); switch (c) { case OPT_LIST_SDOS: do_list_sdos = 1; opt_sdo_tag = optarg; action_count++; break; case OPT_LIST_APPLICATIONS: do_list_apps = 1; action_count++; break; case OPT_BIND_TO_AID: opt_bind_to_aid = optarg; break; case OPT_READER: opt_reader = optarg; break; case 'v': verbose++; break; } } if (action_count == 0) util_print_usage_and_die(app_name, options, option_help, NULL); 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) { fprintf(stderr, "Failed to establish context: %s\n", sc_strerror(r)); return 1; } if (action_count <= 0) goto end; err = util_connect_card(ctx, &card, opt_reader, opt_wait); if (err) goto end; if (opt_bind_to_aid) { struct sc_aid aid; 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; } r = sc_pkcs15_bind(card, &aid, &p15card); } 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))) goto end; action_count--; } if (do_list_apps) { if ((err = list_apps(stdout))) goto end; action_count--; } end: if (p15card) sc_pkcs15_unbind(p15card); if (card) { sc_unlock(card); sc_disconnect_card(card); } sc_release_context(ctx); return err; } OpenSC-0.26.1/src/tools/netkey-tool.c000066400000000000000000000463631474147347300173260ustar00rootroot00000000000000/* * Netkey-Tool for Telesec Netkey E4 cards. * * Copyright (C) 2005, Peter Koch * * 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 #include #include #include "libopensc/opensc.h" #include "libopensc/log.h" static struct { const char *path; int readonly; const char *label; } certlist[]={ {"DF01C000", 1, "Telesec Signatur Zertifikat"}, {"DF014331", 0, "User Signatur Zertifikat1"}, {"DF014332", 0, "User Signatur Zertifikat2"}, {"DF01C100", 1, "Telesec Authentifizierungs Zertifikat"}, {"DF014371", 0, "User Authentifizierungs Zertifikat1"}, {"DF014372", 0, "User Authentifizierungs Zertifikat2"}, {"DF01C200", 1, "Telesec Verschluesselungs Zertifikat"}, {"DF0143B1", 0, "User Verschluesselungs Zertifikat1"}, {"DF0143B2", 0, "User Verschluesselungs Zertifikat2"}, }; static struct { const char *path; const char *name; const char *label; int p1, p2; int tries; size_t len; u8 value[32]; } pinlist[]={ {"3F005000", "pin", "global PIN", 1,-1, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, {"3F005001", "puk", "global PUK", -1,-1, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, {"3F00DF015080", "pin0", "local PIN0", 3, 0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, {"3F00DF015081", "pin1", "local PIN1", 0,-1, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, }; static void show_pin(sc_card_t *card, int pin) { sc_path_t p; sc_file_t *f; struct sc_apdu a; int i, max; sc_format_path(pinlist[pin].path,&p); if((i=sc_select_file(card,&p,&f))<0){ printf("\nCannot select PIN-file %s, is this a NetKey-Card ??\n", pinlist[pin].path); return; } 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("\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] ); return; } pinlist[pin].tries=f->prop_attr[3], max=f->prop_attr[4]; if(pinlist[pin].len){ sc_format_apdu(card, &a, SC_APDU_CASE_3_SHORT, 0x20, 0x00, f->prop_attr[2] | (pin>1 ? 0x80 : 0x00) ); a.data=pinlist[pin].value, a.lc=a.datalen=pinlist[pin].len; } else { sc_format_apdu(card, &a, SC_APDU_CASE_1, 0x20, 0x00, f->prop_attr[2] | (pin>1 ? 0x80 : 0x00) ); } if((i=sc_transmit_apdu(card, &a))<0){ printf("\nsc_transmit_apdu() failed, %s\n", sc_strerror(i)); return; } printf("%s: %d tries left, %d tries max, ", pinlist[pin].label, pinlist[pin].tries, max); if(a.sw1==0x63 && (a.sw2&0xF0)==0xC0) printf("not verified\n"); else if(a.sw1==0x90 && a.sw2==0x00) printf("verified\n"); else if(a.sw1==0x69 && a.sw2==0x83) printf("blocked\n"); else if(a.sw1==0x69 && a.sw2==0x85) printf("NullPin\n"); else printf("Error %02X%02X\n", a.sw1, a.sw2); } static void show_certs(sc_card_t *card) { sc_path_t p; sc_file_t *f; X509 *c; u8 buf[2000]; const u8 *q; int j; size_t i; printf("\n"); for(i=0;itype!=SC_FILE_TYPE_WORKING_EF || f->ef_structure!=SC_FILE_EF_TRANSPARENT){ printf(", Invalid Cert-file: Type=%d, EF-Structure=%d\n", f->type, f->ef_structure); continue; } if((j=sc_read_binary(card,0,buf,f->size,0))<0){ printf(", Cannot read Cert-file, %s\n", sc_strerror(j)); continue; } printf(", Maxlen=%lu", (unsigned long) f->size); q=buf; if(q[0]==0x30 && q[1]==0x82){ 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]; if (X509_NAME_get_text_by_NID(X509_get_subject_name(c), NID_commonName, buf2, sizeof(buf2)) < 0) { sc_log_openssl(card->ctx); printf(" Invalid Subject-CN\n"); X509_free(c); continue; } printf(" Subject-CN: %s\n", buf2); if (X509_NAME_get_text_by_NID(X509_get_issuer_name(c), NID_commonName, buf2, sizeof(buf2)) < 0) { sc_log_openssl(card->ctx); printf(" Invalid Issuer-CN\n"); X509_free(c); continue; } printf(" Issuer-CN: %s\n", buf2); X509_free(c); } else { sc_log_openssl(card->ctx); printf(" Invalid Certificate-Data\n"); } } else printf(", empty\n"); } } static void show_initial_puk(sc_card_t *card) { sc_path_t p; sc_file_t *f; struct sc_apdu a; u8 buf1[128], buf2[128]; int i; printf("\nReading encrypted Initial-PUK-file: "); sc_format_path("3F004350",&p); if((i=sc_select_file(card,&p,&f))<0){ 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 encrypted Initial-PUK-file, %s\n", sc_strerror(i)); return; } 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)); return; } sc_format_apdu(card, &a, SC_APDU_CASE_3_SHORT, 0x22, 0xC1, 0xB8); buf2[0]=0x80, buf2[1]=0x01, buf2[2]=0x10, buf2[3]=0x84, buf2[4]=0x01, buf2[5]=0x81; a.data=buf2, a.lc=a.datalen=6; if((i=sc_transmit_apdu(card, &a))<0){ printf("sc_transmit_apdu(MSE) failed, %s\n", sc_strerror(i)); return; } if(a.sw1!=0x90 && a.sw2!=0x00){ printf("MSE=%02X%02X\n", a.sw1, a.sw2); return; } sc_format_apdu(card, &a, SC_APDU_CASE_4_SHORT, 0x2A, 0x84, 0x80); a.data=buf1, a.lc=a.datalen=128; a.resp=buf2, a.le=a.resplen=128; if((i=sc_transmit_apdu(card, &a))<0){ printf("sc_transmit_apdu(PSO) failed, %s\n", sc_strerror(i)); return; } if(a.sw1!=0x90 && a.sw2!=0x00){ printf("PSO=%02X%02X\n", a.sw1, a.sw2); return; } printf("OK ==> Initial-PUK:"); for(i=120;i<128;++i) printf("%c",buf2[i]); printf("\n"); } static void show_card(sc_card_t *card) { sc_path_t path; sc_file_t *file; u8 buf[100]; int i, len; sc_format_path("3F002F02",&path); if((i=sc_select_file(card,&path,&file))<0){ printf("\nCannot select Serial-Number 2F02, is this a NetKey-Card ??\n"); return; } 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("\nInvalid Serial-Number: Type=%d, EF-Structure=%d, Size=%lu\n", file->type, file->ef_structure, (unsigned long) file->size ); return; } printf("\nSerial-Number: "); for(i=2;i<11;++i) printf("%02X", buf[i]); printf("%X\n\n", buf[11]>>4); for(i=0;i<4;++i) show_pin(card, i); /* printf("%s: %u tries left, %u tries max, %s\n", pinlist[i].label, pinlist[i].tries, max, status); */ if(pinlist[0].len) show_initial_puk(card); } 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; struct sc_apdu a; u8 ref; int i; printf("\n%s %s with %s: ", do_change ? "Changing" : "Unblocking", pinlist[pin1].label, pinlist[pin2].label); sc_format_path(pinlist[pin1].path,&p); if((i=sc_select_file(card,&p,&f))<0){ printf("\nCannot select %s, %s\n", pinlist[pin1].label, sc_strerror(i)); return; } ref=f->prop_attr[2] | (strlen(pinlist[pin1].path)>8 ? 0x80 : 0x00); if(do_change){ sc_format_apdu(card, &a, SC_APDU_CASE_3_SHORT, 0x24, 0x01, ref); a.data=newpin, a.lc=a.datalen=newlen; } else { sc_format_apdu(card, &a, SC_APDU_CASE_1, 0x2C, 0x03, ref); } if((i=sc_transmit_apdu(card, &a))<0){ printf("\nsc_transmit_apdu() failed, %s\n", sc_strerror(i)); return; } if(a.sw1!=0x90 && a.sw2!=0x00){ printf("%02X%02X\n", a.sw1, a.sw2); return; } printf("OK\n"); } 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]; size_t i; int r; printf("\nSetting initial PIN-value: "); sc_format_path(pinlist[0].path,&p); 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 ((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){ printf("global PIN is not in NullPin-state (%02X%02X)\n", a.sw1, a.sw2); return; } sc_format_apdu(card, &a, SC_APDU_CASE_3_SHORT, 0x24, 0x00, ref); for(i=0;i<6;++i) buf[i]=0; for(i=0;isize,0))<0){ printf("Cannot read Cert, %s\n", sc_strerror(len)); return; } 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 %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, long cert, char *file) { sc_path_t p; sc_file_t *f; FILE *fp; X509 *c; u8 buf[1536], *q; int i, len; printf("\nReading Cert from %s: ", file); fflush(stdout); if((fp=fopen(file,"r"))==NULL){ printf("Cannot open file, %s\n", strerror(errno)); return; } c=PEM_read_X509(fp,NULL,NULL,NULL); fclose(fp); if(c==NULL){ printf("file does not contain PEM-encoded certificate\n"); return; } 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)) i2d_X509(c,&q); X509_free(c); if(len<=0 || len>(int)sizeof(buf)){ printf("certificate too long or invalid (Len=%d)\n", len); return; } sc_format_path(certlist[cert].path,&p); if((i=sc_select_file(card,&p,&f))<0){ printf("cannot select certfile, %s\n", sc_strerror(i)); return; } if((i=sc_update_binary(card,0,buf,len,0))<0){ printf("cannot store cert, %s\n", sc_strerror(i)); return; } printf("OK\n"); } static long pin_string2int(char *s) { long i; for (i = 0; (size_t)i < sizeof(pinlist) / sizeof(pinlist[0]); ++i) if (!strcasecmp(pinlist[i].name, s)) return i; return -1; } static void set_pin(u8 *data, size_t *pinlen, char *pin) { int hex; size_t i, len; len=strlen(pin); hex=(len>=5 && len%3==2); if(hex){ *pinlen = sizeof (pinlist[0].value); sc_hex_to_bin(pin, data, pinlen); } else { len=strlen(pin); if(len>32) len=32; for(i=0;i\n"); fprintf(stderr,"usage: %s command\n", argv[0]); fprintf(stderr,"\nOptions:\n"); fprintf(stderr," -v : verbose, may be specified several times\n"); fprintf(stderr," --reader , -r : use reader num (default 0)\n"); fprintf(stderr," --pin , -p : current value of global PIN\n"); fprintf(stderr," --puk , -u : current value of global PUK\n"); fprintf(stderr," --pin0 , -0 : current value of local PIN0\n"); fprintf(stderr," --pin1 , -1 : current value of local PIN1\n"); fprintf(stderr,"\nCommands:\n"); fprintf(stderr," unblock {pin | pin0 | pin1}\n"); fprintf(stderr," change {pin | puk | pin0 | pin1} \n"); fprintf(stderr," nullpin \n"); fprintf(stderr," cert \n"); fprintf(stderr," cert \n"); fprintf(stderr,"\nExamples:\n"); fprintf(stderr,"list PINs and Certs without changing anything. Try this first!!\n"); fprintf(stderr," %s\n", argv[0]); fprintf(stderr,"\nlist PINs and Certs and initial PUK-value (after verification of global PIN)\n"); fprintf(stderr," %s --pin 123456\n", argv[0]); fprintf(stderr,"\nchange local PIN0 to 654321 after verification of global PIN\n"); fprintf(stderr," %s --pin 123456 change pin0 654321\n", argv[0]); fprintf(stderr,"\nchange global PIN from hex 01:02:03:04:05:06 to ascii 123456\n"); fprintf(stderr," %s --pin 01:02:03:04:05:06 change pin 123456\n", argv[0]); fprintf(stderr,"\nunblock global PIN with global PUK\n"); fprintf(stderr," %s --puk 12345678 unblock pin\n", argv[0]); fprintf(stderr,"\nset global PIN to initial value when in NullPin-state\n"); fprintf(stderr," %s nullpin 123456\n", argv[0]); 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 careful - this tool may destroy your card\n"); fprintf(stderr,"\nQuestions? Comments? ==> opensc-user@opensc-project.org\n"); exit(1); } if(optind==argc-2 && !strcmp(argv[optind],"unblock")){ ++optind, do_unblock=1; pin_nr=pin_string2int(argv[optind++]); if(pin_nr<0 || pin_nr==1) ++oerr; } if(optind==argc-3 && !strcmp(argv[optind],"change")){ ++optind, do_change=1; pin_nr=pin_string2int(argv[optind++]); if(pin_nr<0 || pin_nr>3) ++oerr; set_pin(newpin,&newlen,argv[optind++]); } if(optind==argc-2 && !strcmp(argv[optind],"nullpin")){ ++optind, do_nullpin=1; set_pin(newpin,&newlen,argv[optind++]); } if(optind==argc-3 && !strcmp(argv[optind],"cert")){ ++optind; 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); if(!argv[optind][0] || *p || cert_nr<0 || cert_nr>=(int)(sizeof(certlist)/sizeof(certlist[0]))) ++oerr; } optind+=2; } if(oerr || optind!=argc){ fprintf(stderr,"%s: invalid usage, try --help\n", argv[0]); exit(1); } memset(&ctx_param, 0, sizeof(ctx_param)); ctx_param.ver = 0; ctx_param.app_name = argv[0]; r = sc_context_create(&ctx, &ctx_param); if(r < 0){ fprintf(stderr,"Establish-Context failed: %s\n", sc_strerror(r)); exit(1); } ctx->debug = debug; if(ctx->debug>0) printf("Context for application \"%s\" created, Debug=%d\n", ctx->app_name, ctx->debug); for(i=0;ctx->card_drivers[i];++i) 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); for (i = 0; i < card->atr.len; ++i) printf("%c%02X", i?':':' ', card->atr.value[i]); printf("\n"); 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){ 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) ){ fprintf(stderr, "\nNeed %s", do_change ? pinlist[pin_nr].label : pinlist[i1].label); if(do_change && i1>=0) fprintf(stderr, " or %s", pinlist[i1].label); if(i2>=0) fprintf(stderr, " or %s", pinlist[i2].label); fprintf(stderr, " to %s %s\n", do_change ? "change" : "unblock", pinlist[pin_nr].label); } else { if(do_change && pinlist[pin_nr].len) i1=pin_nr; if(i1<0 || !pinlist[i1].len) i1=i2; handle_change(card, pin_nr, i1, do_change, newpin, newlen); } } if(do_nullpin){ handle_nullpin(card, newpin, newlen); show_initial_puk(card); } if(do_readcert) handle_readcert(card, cert_nr, certfile); if(do_writecert){ if(certlist[cert_nr].readonly){ 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 %ld\n", pinlist[0].label, pinlist[3].label, cert_nr ); } else handle_writecert(card, cert_nr, certfile); } if(do_unblock+do_change+do_nullpin+do_readcert==0) show_certs(card); sc_unlock(card); sc_disconnect_card(card); sc_release_context(ctx); exit(0); } OpenSC-0.26.1/src/tools/npa-tool-cmdline.c000066400000000000000000002057471474147347300202210ustar00rootroot00000000000000/* File autogenerated by gengetopt version 2.23 generated with the following command: /opt/homebrew/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: 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 "npa-tool-cmdline.h" const char *gengetopt_args_info_purpose = ""; const char *gengetopt_args_info_usage = "Usage: npa-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", "\nPassword Authenticated Connection Establishment (PACE):", " -p, --pin[=STRING] Run PACE with (transport) eID-PIN", " -u, --puk[=STRING] Run PACE with PUK", " -c, --can[=STRING] Run PACE with CAN", " -m, --mrz[=STRING] Run PACE with MRZ (insert MRZ without newlines)", " --env Whether to use environment variables PIN, PUK,\n CAN, MRZ and NEWPIN. You may want to clean\n your environment before enabling this.\n (default=off)", "\nPIN management:", " -N, --new-pin[=STRING] Install a new PIN", " -R, --resume Resume eID-PIN (uses CAN to activate last\n retry) (default=off)", " -U, --unblock Unblock PIN (uses PUK to activate three more\n retries) (default=off)", "\nTerminal Authentication (TA) and Chip Authentication (CA):", " -C, --cv-certificate=FILENAME Card Verifiable Certificate to create a\n certificate chain. Can be used multiple times\n (order is important).", " --cert-desc=HEX_STRING Certificate description to show for Terminal\n Authentication", " --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=`')", " --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)", "\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)", "\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", " --verify-community=HEX_STRING\n Verify community ID with a reference ID", "\nSpecial options, not always useful:", " -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)", "\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->pin_given = 0 ; args_info->puk_given = 0 ; args_info->can_given = 0 ; args_info->mrz_given = 0 ; args_info->env_given = 0 ; args_info->new_pin_given = 0 ; args_info->resume_given = 0 ; args_info->unblock_given = 0 ; args_info->cv_certificate_given = 0 ; args_info->cert_desc_given = 0 ; args_info->chat_given = 0 ; args_info->auxiliary_data_given = 0 ; args_info->private_key_given = 0 ; args_info->cvc_dir_given = 0 ; args_info->x509_dir_given = 0 ; args_info->disable_ta_checks_given = 0 ; args_info->disable_ca_checks_given = 0 ; args_info->read_dg1_given = 0 ; args_info->read_dg2_given = 0 ; args_info->read_dg3_given = 0 ; args_info->read_dg4_given = 0 ; args_info->read_dg5_given = 0 ; args_info->read_dg6_given = 0 ; args_info->read_dg7_given = 0 ; args_info->read_dg8_given = 0 ; args_info->read_dg9_given = 0 ; args_info->read_dg10_given = 0 ; args_info->read_dg11_given = 0 ; args_info->read_dg12_given = 0 ; args_info->read_dg13_given = 0 ; args_info->read_dg14_given = 0 ; args_info->read_dg15_given = 0 ; args_info->read_dg16_given = 0 ; args_info->read_dg17_given = 0 ; args_info->read_dg18_given = 0 ; args_info->read_dg19_given = 0 ; args_info->read_dg20_given = 0 ; args_info->read_dg21_given = 0 ; args_info->write_dg17_given = 0 ; args_info->write_dg18_given = 0 ; args_info->write_dg19_given = 0 ; args_info->write_dg20_given = 0 ; args_info->write_dg21_given = 0 ; args_info->verify_validity_given = 0 ; args_info->older_than_given = 0 ; args_info->verify_community_given = 0 ; args_info->break_given = 0 ; args_info->translate_given = 0 ; args_info->tr_03110v201_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->pin_arg = NULL; args_info->pin_orig = NULL; args_info->puk_arg = NULL; args_info->puk_orig = NULL; args_info->can_arg = NULL; args_info->can_orig = NULL; args_info->mrz_arg = NULL; args_info->mrz_orig = NULL; args_info->env_flag = 0; args_info->new_pin_arg = NULL; args_info->new_pin_orig = NULL; args_info->resume_flag = 0; args_info->unblock_flag = 0; args_info->cv_certificate_arg = NULL; args_info->cv_certificate_orig = NULL; args_info->cert_desc_arg = NULL; args_info->cert_desc_orig = NULL; args_info->chat_arg = NULL; args_info->chat_orig = NULL; args_info->auxiliary_data_arg = NULL; 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 (""); args_info->cvc_dir_orig = NULL; 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->read_dg1_flag = 0; args_info->read_dg2_flag = 0; args_info->read_dg3_flag = 0; args_info->read_dg4_flag = 0; args_info->read_dg5_flag = 0; args_info->read_dg6_flag = 0; args_info->read_dg7_flag = 0; args_info->read_dg8_flag = 0; args_info->read_dg9_flag = 0; args_info->read_dg10_flag = 0; args_info->read_dg11_flag = 0; args_info->read_dg12_flag = 0; args_info->read_dg13_flag = 0; args_info->read_dg14_flag = 0; args_info->read_dg15_flag = 0; args_info->read_dg16_flag = 0; args_info->read_dg17_flag = 0; args_info->read_dg18_flag = 0; args_info->read_dg19_flag = 0; args_info->read_dg20_flag = 0; args_info->read_dg21_flag = 0; args_info->write_dg17_arg = NULL; args_info->write_dg17_orig = NULL; args_info->write_dg18_arg = NULL; args_info->write_dg18_orig = NULL; args_info->write_dg19_arg = NULL; args_info->write_dg19_orig = NULL; args_info->write_dg20_arg = NULL; args_info->write_dg20_orig = NULL; args_info->write_dg21_arg = NULL; args_info->write_dg21_orig = NULL; args_info->verify_validity_arg = NULL; args_info->verify_validity_orig = NULL; args_info->older_than_arg = NULL; args_info->older_than_orig = NULL; args_info->verify_community_arg = NULL; args_info->verify_community_orig = NULL; args_info->break_flag = 0; args_info->translate_arg = gengetopt_strdup ("stdin"); args_info->translate_orig = NULL; args_info->tr_03110v201_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->pin_help = gengetopt_args_info_help[5] ; args_info->puk_help = gengetopt_args_info_help[6] ; args_info->can_help = gengetopt_args_info_help[7] ; args_info->mrz_help = gengetopt_args_info_help[8] ; args_info->env_help = gengetopt_args_info_help[9] ; args_info->new_pin_help = gengetopt_args_info_help[11] ; args_info->resume_help = gengetopt_args_info_help[12] ; args_info->unblock_help = gengetopt_args_info_help[13] ; args_info->cv_certificate_help = gengetopt_args_info_help[15] ; args_info->cv_certificate_min = 0; args_info->cv_certificate_max = 0; args_info->cert_desc_help = gengetopt_args_info_help[16] ; args_info->chat_help = gengetopt_args_info_help[17] ; args_info->auxiliary_data_help = gengetopt_args_info_help[18] ; args_info->private_key_help = gengetopt_args_info_help[19] ; args_info->cvc_dir_help = gengetopt_args_info_help[20] ; 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] ; } 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 { 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; } 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_string_field (&(args_info->pin_arg)); free_string_field (&(args_info->pin_orig)); free_string_field (&(args_info->puk_arg)); free_string_field (&(args_info->puk_orig)); free_string_field (&(args_info->can_arg)); free_string_field (&(args_info->can_orig)); free_string_field (&(args_info->mrz_arg)); free_string_field (&(args_info->mrz_orig)); free_string_field (&(args_info->new_pin_arg)); free_string_field (&(args_info->new_pin_orig)); free_multiple_string_field (args_info->cv_certificate_given, &(args_info->cv_certificate_arg), &(args_info->cv_certificate_orig)); free_string_field (&(args_info->cert_desc_arg)); free_string_field (&(args_info->cert_desc_orig)); free_string_field (&(args_info->chat_arg)); free_string_field (&(args_info->chat_orig)); free_string_field (&(args_info->auxiliary_data_arg)); free_string_field (&(args_info->auxiliary_data_orig)); free_string_field (&(args_info->private_key_arg)); free_string_field (&(args_info->private_key_orig)); free_string_field (&(args_info->cvc_dir_arg)); 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->write_dg17_arg)); free_string_field (&(args_info->write_dg17_orig)); free_string_field (&(args_info->write_dg18_arg)); free_string_field (&(args_info->write_dg18_orig)); free_string_field (&(args_info->write_dg19_arg)); free_string_field (&(args_info->write_dg19_orig)); free_string_field (&(args_info->write_dg20_arg)); free_string_field (&(args_info->write_dg20_orig)); free_string_field (&(args_info->write_dg21_arg)); free_string_field (&(args_info->write_dg21_orig)); free_string_field (&(args_info->verify_validity_arg)); free_string_field (&(args_info->verify_validity_orig)); free_string_field (&(args_info->older_than_arg)); free_string_field (&(args_info->older_than_orig)); free_string_field (&(args_info->verify_community_arg)); free_string_field (&(args_info->verify_community_orig)); free_string_field (&(args_info->translate_arg)); free_string_field (&(args_info->translate_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->pin_given) write_into_file(outfile, "pin", args_info->pin_orig, 0); if (args_info->puk_given) write_into_file(outfile, "puk", args_info->puk_orig, 0); if (args_info->can_given) write_into_file(outfile, "can", args_info->can_orig, 0); if (args_info->mrz_given) write_into_file(outfile, "mrz", args_info->mrz_orig, 0); if (args_info->env_given) write_into_file(outfile, "env", 0, 0 ); if (args_info->new_pin_given) write_into_file(outfile, "new-pin", args_info->new_pin_orig, 0); if (args_info->resume_given) write_into_file(outfile, "resume", 0, 0 ); if (args_info->unblock_given) write_into_file(outfile, "unblock", 0, 0 ); write_multiple_into_file(outfile, args_info->cv_certificate_given, "cv-certificate", args_info->cv_certificate_orig, 0); if (args_info->cert_desc_given) write_into_file(outfile, "cert-desc", args_info->cert_desc_orig, 0); if (args_info->chat_given) write_into_file(outfile, "chat", args_info->chat_orig, 0); if (args_info->auxiliary_data_given) write_into_file(outfile, "auxiliary-data", args_info->auxiliary_data_orig, 0); if (args_info->private_key_given) write_into_file(outfile, "private-key", args_info->private_key_orig, 0); if (args_info->cvc_dir_given) write_into_file(outfile, "cvc-dir", args_info->cvc_dir_orig, 0); if (args_info->x509_dir_given) write_into_file(outfile, "x509-dir", args_info->x509_dir_orig, 0); if (args_info->disable_ta_checks_given) 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->read_dg1_given) write_into_file(outfile, "read-dg1", 0, 0 ); if (args_info->read_dg2_given) write_into_file(outfile, "read-dg2", 0, 0 ); if (args_info->read_dg3_given) write_into_file(outfile, "read-dg3", 0, 0 ); if (args_info->read_dg4_given) write_into_file(outfile, "read-dg4", 0, 0 ); if (args_info->read_dg5_given) write_into_file(outfile, "read-dg5", 0, 0 ); if (args_info->read_dg6_given) write_into_file(outfile, "read-dg6", 0, 0 ); if (args_info->read_dg7_given) write_into_file(outfile, "read-dg7", 0, 0 ); if (args_info->read_dg8_given) write_into_file(outfile, "read-dg8", 0, 0 ); if (args_info->read_dg9_given) write_into_file(outfile, "read-dg9", 0, 0 ); if (args_info->read_dg10_given) write_into_file(outfile, "read-dg10", 0, 0 ); if (args_info->read_dg11_given) write_into_file(outfile, "read-dg11", 0, 0 ); if (args_info->read_dg12_given) write_into_file(outfile, "read-dg12", 0, 0 ); if (args_info->read_dg13_given) write_into_file(outfile, "read-dg13", 0, 0 ); if (args_info->read_dg14_given) write_into_file(outfile, "read-dg14", 0, 0 ); if (args_info->read_dg15_given) write_into_file(outfile, "read-dg15", 0, 0 ); if (args_info->read_dg16_given) write_into_file(outfile, "read-dg16", 0, 0 ); if (args_info->read_dg17_given) write_into_file(outfile, "read-dg17", 0, 0 ); if (args_info->read_dg18_given) write_into_file(outfile, "read-dg18", 0, 0 ); if (args_info->read_dg19_given) write_into_file(outfile, "read-dg19", 0, 0 ); if (args_info->read_dg20_given) write_into_file(outfile, "read-dg20", 0, 0 ); if (args_info->read_dg21_given) write_into_file(outfile, "read-dg21", 0, 0 ); if (args_info->write_dg17_given) write_into_file(outfile, "write-dg17", args_info->write_dg17_orig, 0); if (args_info->write_dg18_given) write_into_file(outfile, "write-dg18", args_info->write_dg18_orig, 0); if (args_info->write_dg19_given) write_into_file(outfile, "write-dg19", args_info->write_dg19_orig, 0); if (args_info->write_dg20_given) write_into_file(outfile, "write-dg20", args_info->write_dg20_orig, 0); if (args_info->write_dg21_given) write_into_file(outfile, "write-dg21", args_info->write_dg21_orig, 0); if (args_info->verify_validity_given) write_into_file(outfile, "verify-validity", args_info->verify_validity_orig, 0); if (args_info->older_than_given) write_into_file(outfile, "older-than", args_info->older_than_orig, 0); if (args_info->verify_community_given) write_into_file(outfile, "verify-community", args_info->verify_community_orig, 0); if (args_info->break_given) write_into_file(outfile, "break", 0, 0 ); if (args_info->translate_given) write_into_file(outfile, "translate", args_info->translate_orig, 0); if (args_info->tr_03110v201_given) write_into_file(outfile, "tr-03110v201", 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 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 (check_multiple_option_occurrences(prog_name, args_info->cv_certificate_given, args_info->cv_certificate_min, args_info->cv_certificate_max, "'--cv-certificate' ('-C')")) 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 */ } /** * @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_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_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_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; } } } } 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 * cv_certificate_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' }, { "pin", 2, NULL, 'p' }, { "puk", 2, NULL, 'u' }, { "can", 2, NULL, 'c' }, { "mrz", 2, NULL, 'm' }, { "env", 0, NULL, 0 }, { "new-pin", 2, NULL, 'N' }, { "resume", 0, NULL, 'R' }, { "unblock", 0, NULL, 'U' }, { "cv-certificate", 1, NULL, 'C' }, { "cert-desc", 1, NULL, 0 }, { "chat", 1, NULL, 0 }, { "auxiliary-data", 1, NULL, 'A' }, { "private-key", 1, NULL, 'P' }, { "cvc-dir", 1, NULL, 0 }, { "x509-dir", 1, NULL, 0 }, { "disable-ta-checks", 0, NULL, 0 }, { "disable-ca-checks", 0, NULL, 0 }, { "read-dg1", 0, NULL, 0 }, { "read-dg2", 0, NULL, 0 }, { "read-dg3", 0, NULL, 0 }, { "read-dg4", 0, NULL, 0 }, { "read-dg5", 0, NULL, 0 }, { "read-dg6", 0, NULL, 0 }, { "read-dg7", 0, NULL, 0 }, { "read-dg8", 0, NULL, 0 }, { "read-dg9", 0, NULL, 0 }, { "read-dg10", 0, NULL, 0 }, { "read-dg11", 0, NULL, 0 }, { "read-dg12", 0, NULL, 0 }, { "read-dg13", 0, NULL, 0 }, { "read-dg14", 0, NULL, 0 }, { "read-dg15", 0, NULL, 0 }, { "read-dg16", 0, NULL, 0 }, { "read-dg17", 0, NULL, 0 }, { "read-dg18", 0, NULL, 0 }, { "read-dg19", 0, NULL, 0 }, { "read-dg20", 0, NULL, 0 }, { "read-dg21", 0, NULL, 0 }, { "write-dg17", 1, NULL, 0 }, { "write-dg18", 1, NULL, 0 }, { "write-dg19", 1, NULL, 0 }, { "write-dg20", 1, NULL, 0 }, { "write-dg21", 1, NULL, 0 }, { "verify-validity", 1, NULL, 0 }, { "older-than", 1, NULL, 0 }, { "verify-community", 1, NULL, 0 }, { "break", 0, NULL, 'b' }, { "translate", 1, NULL, 't' }, { "tr-03110v201", 0, NULL, 0 }, { 0, 0, 0, 0 } }; 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. */ 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': /* Run PACE with (transport) eID-PIN. */ if (update_arg( (void *)&(args_info->pin_arg), &(args_info->pin_orig), &(args_info->pin_given), &(local_args_info.pin_given), optarg, 0, 0, ARG_STRING, check_ambiguity, override, 0, 0, "pin", 'p', additional_error)) goto failure; break; case 'u': /* Run PACE with PUK. */ if (update_arg( (void *)&(args_info->puk_arg), &(args_info->puk_orig), &(args_info->puk_given), &(local_args_info.puk_given), optarg, 0, 0, ARG_STRING, check_ambiguity, override, 0, 0, "puk", 'u', additional_error)) goto failure; break; case 'c': /* Run PACE with CAN. */ if (update_arg( (void *)&(args_info->can_arg), &(args_info->can_orig), &(args_info->can_given), &(local_args_info.can_given), optarg, 0, 0, ARG_STRING, check_ambiguity, override, 0, 0, "can", 'c', additional_error)) goto failure; break; case 'm': /* Run PACE with MRZ (insert MRZ without newlines). */ if (update_arg( (void *)&(args_info->mrz_arg), &(args_info->mrz_orig), &(args_info->mrz_given), &(local_args_info.mrz_given), optarg, 0, 0, ARG_STRING, check_ambiguity, override, 0, 0, "mrz", 'm', additional_error)) goto failure; break; case 'N': /* Install a new PIN. */ if (update_arg( (void *)&(args_info->new_pin_arg), &(args_info->new_pin_orig), &(args_info->new_pin_given), &(local_args_info.new_pin_given), optarg, 0, 0, ARG_STRING, check_ambiguity, override, 0, 0, "new-pin", 'N', additional_error)) goto failure; break; case 'R': /* Resume eID-PIN (uses CAN to activate last retry). */ if (update_arg((void *)&(args_info->resume_flag), 0, &(args_info->resume_given), &(local_args_info.resume_given), optarg, 0, 0, ARG_FLAG, check_ambiguity, override, 1, 0, "resume", 'R', additional_error)) goto failure; break; case 'U': /* Unblock PIN (uses PUK to activate three more retries). */ if (update_arg((void *)&(args_info->unblock_flag), 0, &(args_info->unblock_given), &(local_args_info.unblock_given), optarg, 0, 0, ARG_FLAG, check_ambiguity, override, 1, 0, "unblock", 'U', additional_error)) goto failure; break; case 'C': /* Card Verifiable Certificate to create a certificate chain. Can be used multiple times (order is important).. */ if (update_multiple_arg_temp(&cv_certificate_list, &(local_args_info.cv_certificate_given), optarg, 0, 0, ARG_STRING, "cv-certificate", 'C', additional_error)) goto failure; break; case 'A': /* Terminal's auxiliary data (default is determined by verification of validity, age and community ID).. */ if (update_arg( (void *)&(args_info->auxiliary_data_arg), &(args_info->auxiliary_data_orig), &(args_info->auxiliary_data_given), &(local_args_info.auxiliary_data_given), optarg, 0, 0, ARG_STRING, check_ambiguity, override, 0, 0, "auxiliary-data", 'A', additional_error)) goto failure; break; case 'P': /* Terminal's private key. */ if (update_arg( (void *)&(args_info->private_key_arg), &(args_info->private_key_orig), &(args_info->private_key_given), &(local_args_info.private_key_given), optarg, 0, 0, ARG_STRING, check_ambiguity, override, 0, 0, "private-key", 'P', additional_error)) goto failure; break; case 'b': /* Brute force PIN, CAN or PUK. Use together with -p, -a or -u. */ if (update_arg((void *)&(args_info->break_flag), 0, &(args_info->break_given), &(local_args_info.break_given), optarg, 0, 0, ARG_FLAG, check_ambiguity, override, 1, 0, "break", 'b', additional_error)) goto failure; break; case 't': /* File with APDUs of HEX_STRINGs to send through the secure channel. */ if (update_arg( (void *)&(args_info->translate_arg), &(args_info->translate_orig), &(args_info->translate_given), &(local_args_info.translate_given), optarg, 0, "stdin", ARG_STRING, check_ambiguity, override, 0, 0, "translate", 't', additional_error)) goto failure; break; case 0: /* Long option with no short option */ /* Whether to use environment variables PIN, PUK, CAN, MRZ and NEWPIN. You may want to clean your environment before enabling this.. */ if (strcmp (long_options[option_index].name, "env") == 0) { if (update_arg((void *)&(args_info->env_flag), 0, &(args_info->env_given), &(local_args_info.env_given), optarg, 0, 0, ARG_FLAG, check_ambiguity, override, 1, 0, "env", '-', additional_error)) goto failure; } /* Certificate description to show for Terminal Authentication. */ else if (strcmp (long_options[option_index].name, "cert-desc") == 0) { if (update_arg( (void *)&(args_info->cert_desc_arg), &(args_info->cert_desc_orig), &(args_info->cert_desc_given), &(local_args_info.cert_desc_given), optarg, 0, 0, ARG_STRING, check_ambiguity, override, 0, 0, "cert-desc", '-', additional_error)) goto failure; } /* Card holder authorization template to use (default is terminal's CHAT). Use 7F4C0E060904007F000703010203530103 to trigger EAC on the CAT-C (Komfortleser).. */ else if (strcmp (long_options[option_index].name, "chat") == 0) { if (update_arg( (void *)&(args_info->chat_arg), &(args_info->chat_orig), &(args_info->chat_given), &(local_args_info.chat_given), optarg, 0, 0, ARG_STRING, check_ambiguity, override, 0, 0, "chat", '-', additional_error)) goto failure; } /* Where to look for the CVCA's certificate. */ else if (strcmp (long_options[option_index].name, "cvc-dir") == 0) { 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, "", ARG_STRING, check_ambiguity, override, 0, 0, "cvc-dir", '-', additional_error)) goto failure; } /* Where to look for the CSCA's certificate. */ else if (strcmp (long_options[option_index].name, "x509-dir") == 0) { 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, "", ARG_STRING, check_ambiguity, override, 0, 0, "x509-dir", '-', additional_error)) goto failure; } /* Disable checking the validity period of CV certificates. */ else if (strcmp (long_options[option_index].name, "disable-ta-checks") == 0) { if (update_arg((void *)&(args_info->disable_ta_checks_flag), 0, &(args_info->disable_ta_checks_given), &(local_args_info.disable_ta_checks_given), optarg, 0, 0, ARG_FLAG, check_ambiguity, override, 1, 0, "disable-ta-checks", '-', additional_error)) goto failure; } /* Disable passive authentication. */ else if (strcmp (long_options[option_index].name, "disable-ca-checks") == 0) { if (update_arg((void *)&(args_info->disable_ca_checks_flag), 0, &(args_info->disable_ca_checks_given), &(local_args_info.disable_ca_checks_given), optarg, 0, 0, ARG_FLAG, check_ambiguity, override, 1, 0, "disable-ca-checks", '-', additional_error)) goto failure; } /* Read DG 1 (Document Type). */ else if (strcmp (long_options[option_index].name, "read-dg1") == 0) { if (update_arg((void *)&(args_info->read_dg1_flag), 0, &(args_info->read_dg1_given), &(local_args_info.read_dg1_given), optarg, 0, 0, ARG_FLAG, check_ambiguity, override, 1, 0, "read-dg1", '-', additional_error)) goto failure; } /* Read DG 2 (Issuing State). */ else if (strcmp (long_options[option_index].name, "read-dg2") == 0) { if (update_arg((void *)&(args_info->read_dg2_flag), 0, &(args_info->read_dg2_given), &(local_args_info.read_dg2_given), optarg, 0, 0, ARG_FLAG, check_ambiguity, override, 1, 0, "read-dg2", '-', additional_error)) goto failure; } /* Read DG 3 (Date of Expiry). */ else if (strcmp (long_options[option_index].name, "read-dg3") == 0) { if (update_arg((void *)&(args_info->read_dg3_flag), 0, &(args_info->read_dg3_given), &(local_args_info.read_dg3_given), optarg, 0, 0, ARG_FLAG, check_ambiguity, override, 1, 0, "read-dg3", '-', additional_error)) goto failure; } /* Read DG 4 (Given Names). */ else if (strcmp (long_options[option_index].name, "read-dg4") == 0) { if (update_arg((void *)&(args_info->read_dg4_flag), 0, &(args_info->read_dg4_given), &(local_args_info.read_dg4_given), optarg, 0, 0, ARG_FLAG, check_ambiguity, override, 1, 0, "read-dg4", '-', additional_error)) goto failure; } /* Read DG 5 (Family Names). */ else if (strcmp (long_options[option_index].name, "read-dg5") == 0) { if (update_arg((void *)&(args_info->read_dg5_flag), 0, &(args_info->read_dg5_given), &(local_args_info.read_dg5_given), optarg, 0, 0, ARG_FLAG, check_ambiguity, override, 1, 0, "read-dg5", '-', additional_error)) goto failure; } /* Read DG 6 (Religious/Artistic Name). */ else if (strcmp (long_options[option_index].name, "read-dg6") == 0) { if (update_arg((void *)&(args_info->read_dg6_flag), 0, &(args_info->read_dg6_given), &(local_args_info.read_dg6_given), optarg, 0, 0, ARG_FLAG, check_ambiguity, override, 1, 0, "read-dg6", '-', additional_error)) goto failure; } /* Read DG 7 (Academic Title). */ else if (strcmp (long_options[option_index].name, "read-dg7") == 0) { if (update_arg((void *)&(args_info->read_dg7_flag), 0, &(args_info->read_dg7_given), &(local_args_info.read_dg7_given), optarg, 0, 0, ARG_FLAG, check_ambiguity, override, 1, 0, "read-dg7", '-', additional_error)) goto failure; } /* Read DG 8 (Date of Birth). */ else if (strcmp (long_options[option_index].name, "read-dg8") == 0) { if (update_arg((void *)&(args_info->read_dg8_flag), 0, &(args_info->read_dg8_given), &(local_args_info.read_dg8_given), optarg, 0, 0, ARG_FLAG, check_ambiguity, override, 1, 0, "read-dg8", '-', additional_error)) goto failure; } /* Read DG 9 (Place of Birth). */ else if (strcmp (long_options[option_index].name, "read-dg9") == 0) { if (update_arg((void *)&(args_info->read_dg9_flag), 0, &(args_info->read_dg9_given), &(local_args_info.read_dg9_given), optarg, 0, 0, ARG_FLAG, check_ambiguity, override, 1, 0, "read-dg9", '-', additional_error)) goto failure; } /* Read DG 10 (Nationality). */ else if (strcmp (long_options[option_index].name, "read-dg10") == 0) { if (update_arg((void *)&(args_info->read_dg10_flag), 0, &(args_info->read_dg10_given), &(local_args_info.read_dg10_given), optarg, 0, 0, ARG_FLAG, check_ambiguity, override, 1, 0, "read-dg10", '-', additional_error)) goto failure; } /* Read DG 11 (Sex). */ else if (strcmp (long_options[option_index].name, "read-dg11") == 0) { if (update_arg((void *)&(args_info->read_dg11_flag), 0, &(args_info->read_dg11_given), &(local_args_info.read_dg11_given), optarg, 0, 0, ARG_FLAG, check_ambiguity, override, 1, 0, "read-dg11", '-', additional_error)) goto failure; } /* Read DG 12 (Optional Data). */ else if (strcmp (long_options[option_index].name, "read-dg12") == 0) { if (update_arg((void *)&(args_info->read_dg12_flag), 0, &(args_info->read_dg12_given), &(local_args_info.read_dg12_given), optarg, 0, 0, ARG_FLAG, check_ambiguity, override, 1, 0, "read-dg12", '-', additional_error)) goto failure; } /* Read DG 13 (Birth Name). */ else if (strcmp (long_options[option_index].name, "read-dg13") == 0) { if (update_arg((void *)&(args_info->read_dg13_flag), 0, &(args_info->read_dg13_given), &(local_args_info.read_dg13_given), optarg, 0, 0, ARG_FLAG, check_ambiguity, override, 1, 0, "read-dg13", '-', additional_error)) goto failure; } /* Read DG 14. */ else if (strcmp (long_options[option_index].name, "read-dg14") == 0) { if (update_arg((void *)&(args_info->read_dg14_flag), 0, &(args_info->read_dg14_given), &(local_args_info.read_dg14_given), optarg, 0, 0, ARG_FLAG, check_ambiguity, override, 1, 0, "read-dg14", '-', additional_error)) goto failure; } /* Read DG 15. */ else if (strcmp (long_options[option_index].name, "read-dg15") == 0) { if (update_arg((void *)&(args_info->read_dg15_flag), 0, &(args_info->read_dg15_given), &(local_args_info.read_dg15_given), optarg, 0, 0, ARG_FLAG, check_ambiguity, override, 1, 0, "read-dg15", '-', additional_error)) goto failure; } /* Read DG 16. */ else if (strcmp (long_options[option_index].name, "read-dg16") == 0) { if (update_arg((void *)&(args_info->read_dg16_flag), 0, &(args_info->read_dg16_given), &(local_args_info.read_dg16_given), optarg, 0, 0, ARG_FLAG, check_ambiguity, override, 1, 0, "read-dg16", '-', additional_error)) goto failure; } /* Read DG 17 (Normal Place of Residence). */ else if (strcmp (long_options[option_index].name, "read-dg17") == 0) { if (update_arg((void *)&(args_info->read_dg17_flag), 0, &(args_info->read_dg17_given), &(local_args_info.read_dg17_given), optarg, 0, 0, ARG_FLAG, check_ambiguity, override, 1, 0, "read-dg17", '-', additional_error)) goto failure; } /* Read DG 18 (Community ID). */ else if (strcmp (long_options[option_index].name, "read-dg18") == 0) { if (update_arg((void *)&(args_info->read_dg18_flag), 0, &(args_info->read_dg18_given), &(local_args_info.read_dg18_given), optarg, 0, 0, ARG_FLAG, check_ambiguity, override, 1, 0, "read-dg18", '-', additional_error)) goto failure; } /* Read DG 19 (Residence Permit I). */ else if (strcmp (long_options[option_index].name, "read-dg19") == 0) { if (update_arg((void *)&(args_info->read_dg19_flag), 0, &(args_info->read_dg19_given), &(local_args_info.read_dg19_given), optarg, 0, 0, ARG_FLAG, check_ambiguity, override, 1, 0, "read-dg19", '-', additional_error)) goto failure; } /* Read DG 20 (Residence Permit II). */ else if (strcmp (long_options[option_index].name, "read-dg20") == 0) { if (update_arg((void *)&(args_info->read_dg20_flag), 0, &(args_info->read_dg20_given), &(local_args_info.read_dg20_given), optarg, 0, 0, ARG_FLAG, check_ambiguity, override, 1, 0, "read-dg20", '-', additional_error)) goto failure; } /* Read DG 21 (Optional Data). */ else if (strcmp (long_options[option_index].name, "read-dg21") == 0) { if (update_arg((void *)&(args_info->read_dg21_flag), 0, &(args_info->read_dg21_given), &(local_args_info.read_dg21_given), optarg, 0, 0, ARG_FLAG, check_ambiguity, override, 1, 0, "read-dg21", '-', additional_error)) goto failure; } /* Write DG 17 (Normal Place of Residence). */ else if (strcmp (long_options[option_index].name, "write-dg17") == 0) { if (update_arg( (void *)&(args_info->write_dg17_arg), &(args_info->write_dg17_orig), &(args_info->write_dg17_given), &(local_args_info.write_dg17_given), optarg, 0, 0, ARG_STRING, check_ambiguity, override, 0, 0, "write-dg17", '-', additional_error)) goto failure; } /* Write DG 18 (Community ID). */ else if (strcmp (long_options[option_index].name, "write-dg18") == 0) { if (update_arg( (void *)&(args_info->write_dg18_arg), &(args_info->write_dg18_orig), &(args_info->write_dg18_given), &(local_args_info.write_dg18_given), optarg, 0, 0, ARG_STRING, check_ambiguity, override, 0, 0, "write-dg18", '-', additional_error)) goto failure; } /* Write DG 19 (Residence Permit I). */ else if (strcmp (long_options[option_index].name, "write-dg19") == 0) { if (update_arg( (void *)&(args_info->write_dg19_arg), &(args_info->write_dg19_orig), &(args_info->write_dg19_given), &(local_args_info.write_dg19_given), optarg, 0, 0, ARG_STRING, check_ambiguity, override, 0, 0, "write-dg19", '-', additional_error)) goto failure; } /* Write DG 20 (Residence Permit II). */ else if (strcmp (long_options[option_index].name, "write-dg20") == 0) { if (update_arg( (void *)&(args_info->write_dg20_arg), &(args_info->write_dg20_orig), &(args_info->write_dg20_given), &(local_args_info.write_dg20_given), optarg, 0, 0, ARG_STRING, check_ambiguity, override, 0, 0, "write-dg20", '-', additional_error)) goto failure; } /* Write DG 21 (Optional Data). */ else if (strcmp (long_options[option_index].name, "write-dg21") == 0) { if (update_arg( (void *)&(args_info->write_dg21_arg), &(args_info->write_dg21_orig), &(args_info->write_dg21_given), &(local_args_info.write_dg21_given), optarg, 0, 0, ARG_STRING, check_ambiguity, override, 0, 0, "write-dg21", '-', additional_error)) goto failure; } /* Verify chip's validity with a reference date. */ else if (strcmp (long_options[option_index].name, "verify-validity") == 0) { if (update_arg( (void *)&(args_info->verify_validity_arg), &(args_info->verify_validity_orig), &(args_info->verify_validity_given), &(local_args_info.verify_validity_given), optarg, 0, 0, ARG_STRING, check_ambiguity, override, 0, 0, "verify-validity", '-', additional_error)) goto failure; } /* Verify age with a reference date. */ else if (strcmp (long_options[option_index].name, "older-than") == 0) { if (update_arg( (void *)&(args_info->older_than_arg), &(args_info->older_than_orig), &(args_info->older_than_given), &(local_args_info.older_than_given), optarg, 0, 0, ARG_STRING, check_ambiguity, override, 0, 0, "older-than", '-', additional_error)) goto failure; } /* Verify community ID with a reference ID. */ else if (strcmp (long_options[option_index].name, "verify-community") == 0) { if (update_arg( (void *)&(args_info->verify_community_arg), &(args_info->verify_community_orig), &(args_info->verify_community_given), &(local_args_info.verify_community_given), optarg, 0, 0, ARG_STRING, check_ambiguity, override, 0, 0, "verify-community", '-', additional_error)) goto failure; } /* Force compliance to BSI TR-03110 version 2.01. */ else if (strcmp (long_options[option_index].name, "tr-03110v201") == 0) { if (update_arg((void *)&(args_info->tr_03110v201_flag), 0, &(args_info->tr_03110v201_given), &(local_args_info.tr_03110v201_given), optarg, 0, 0, ARG_FLAG, check_ambiguity, override, 1, 0, "tr-03110v201", '-', 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->cv_certificate_arg), &(args_info->cv_certificate_orig), args_info->cv_certificate_given, local_args_info.cv_certificate_given, 0, ARG_STRING, cv_certificate_list); args_info->verbose_given += local_args_info.verbose_given; local_args_info.verbose_given = 0; args_info->cv_certificate_given += local_args_info.cv_certificate_given; local_args_info.cv_certificate_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: free_list (cv_certificate_list, 1 ); cmdline_parser_release (&local_args_info); return (EXIT_FAILURE); } /* vim: set ft=c noet ts=8 sts=8 sw=8 tw=80 nojs spell : */ OpenSC-0.26.1/src/tools/npa-tool-cmdline.h000066400000000000000000000560411474147347300202150ustar00rootroot00000000000000/** @file npa-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 NPA_TOOL_CMDLINE_H #define NPA_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 "npa-tool" #endif #ifndef CMDLINE_PARSER_PACKAGE_NAME /** @brief the complete program name (used for help and version) */ #define CMDLINE_PARSER_PACKAGE_NAME "npa-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. */ char * pin_arg; /**< @brief Run PACE with (transport) eID-PIN. */ char * pin_orig; /**< @brief Run PACE with (transport) eID-PIN original value given at command line. */ const char *pin_help; /**< @brief Run PACE with (transport) eID-PIN help description. */ char * puk_arg; /**< @brief Run PACE with PUK. */ char * puk_orig; /**< @brief Run PACE with PUK original value given at command line. */ const char *puk_help; /**< @brief Run PACE with PUK help description. */ char * can_arg; /**< @brief Run PACE with CAN. */ char * can_orig; /**< @brief Run PACE with CAN original value given at command line. */ const char *can_help; /**< @brief Run PACE with CAN help description. */ char * mrz_arg; /**< @brief Run PACE with MRZ (insert MRZ without newlines). */ char * mrz_orig; /**< @brief Run PACE with MRZ (insert MRZ without newlines) original value given at command line. */ const char *mrz_help; /**< @brief Run PACE with MRZ (insert MRZ without newlines) help description. */ int env_flag; /**< @brief Whether to use environment variables PIN, PUK, CAN, MRZ and NEWPIN. You may want to clean your environment before enabling this. (default=off). */ const char *env_help; /**< @brief Whether to use environment variables PIN, PUK, CAN, MRZ and NEWPIN. You may want to clean your environment before enabling this. help description. */ char * new_pin_arg; /**< @brief Install a new PIN. */ char * new_pin_orig; /**< @brief Install a new PIN original value given at command line. */ const char *new_pin_help; /**< @brief Install a new PIN help description. */ int resume_flag; /**< @brief Resume eID-PIN (uses CAN to activate last retry) (default=off). */ const char *resume_help; /**< @brief Resume eID-PIN (uses CAN to activate last retry) help description. */ int unblock_flag; /**< @brief Unblock PIN (uses PUK to activate three more retries) (default=off). */ const char *unblock_help; /**< @brief Unblock PIN (uses PUK to activate three more retries) help description. */ char ** cv_certificate_arg; /**< @brief Card Verifiable Certificate to create a certificate chain. Can be used multiple times (order is important).. */ char ** cv_certificate_orig; /**< @brief Card Verifiable Certificate to create a certificate chain. Can be used multiple times (order is important). original value given at command line. */ unsigned int cv_certificate_min; /**< @brief Card Verifiable Certificate to create a certificate chain. Can be used multiple times (order is important).'s minimum occurreces */ unsigned int cv_certificate_max; /**< @brief Card Verifiable Certificate to create a certificate chain. Can be used multiple times (order is important).'s maximum occurreces */ const char *cv_certificate_help; /**< @brief Card Verifiable Certificate to create a certificate chain. Can be used multiple times (order is important). help description. */ char * cert_desc_arg; /**< @brief Certificate description to show for Terminal Authentication. */ char * cert_desc_orig; /**< @brief Certificate description to show for Terminal Authentication original value given at command line. */ const char *cert_desc_help; /**< @brief Certificate description to show for Terminal Authentication help description. */ char * chat_arg; /**< @brief Card holder authorization template to use (default is terminal's CHAT). Use 7F4C0E060904007F000703010203530103 to trigger EAC on the CAT-C (Komfortleser).. */ char * chat_orig; /**< @brief Card holder authorization template to use (default is terminal's CHAT). Use 7F4C0E060904007F000703010203530103 to trigger EAC on the CAT-C (Komfortleser). original value given at command line. */ const char *chat_help; /**< @brief Card holder authorization template to use (default is terminal's CHAT). Use 7F4C0E060904007F000703010203530103 to trigger EAC on the CAT-C (Komfortleser). help description. */ char * auxiliary_data_arg; /**< @brief Terminal's auxiliary data (default is determined by verification of validity, age and community ID).. */ char * auxiliary_data_orig; /**< @brief Terminal's auxiliary data (default is determined by verification of validity, age and community ID). original value given at command line. */ const char *auxiliary_data_help; /**< @brief Terminal's auxiliary data (default is determined by verification of validity, age and community ID). help description. */ 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=''). */ 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=''). */ 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 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. */ 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. */ char * older_than_arg; /**< @brief Verify age with a reference date. */ char * older_than_orig; /**< @brief Verify age with a reference date original value given at command line. */ const char *older_than_help; /**< @brief Verify age with a reference date help description. */ char * verify_community_arg; /**< @brief Verify community ID with a reference ID. */ char * verify_community_orig; /**< @brief Verify community ID with a reference ID original value given at command line. */ const char *verify_community_help; /**< @brief Verify community ID with a reference ID help description. */ int break_flag; /**< @brief Brute force PIN, CAN or PUK. Use together with -p, -a or -u (default=off). */ const char *break_help; /**< @brief Brute force PIN, CAN or PUK. Use together with -p, -a or -u help description. */ char * translate_arg; /**< @brief File with APDUs of HEX_STRINGs to send through the secure channel (default='stdin'). */ char * translate_orig; /**< @brief File with APDUs of HEX_STRINGs to send through the secure channel original value given at command line. */ 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. */ 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 pin_given ; /**< @brief Whether pin was given. */ unsigned int puk_given ; /**< @brief Whether puk was given. */ unsigned int can_given ; /**< @brief Whether can was given. */ unsigned int mrz_given ; /**< @brief Whether mrz was given. */ unsigned int env_given ; /**< @brief Whether env was given. */ unsigned int new_pin_given ; /**< @brief Whether new-pin was given. */ unsigned int resume_given ; /**< @brief Whether resume was given. */ unsigned int unblock_given ; /**< @brief Whether unblock was given. */ unsigned int cv_certificate_given ; /**< @brief Whether cv-certificate was given. */ unsigned int cert_desc_given ; /**< @brief Whether cert-desc was given. */ unsigned int chat_given ; /**< @brief Whether chat was given. */ unsigned int auxiliary_data_given ; /**< @brief Whether auxiliary-data was given. */ unsigned int private_key_given ; /**< @brief Whether private-key was given. */ unsigned int cvc_dir_given ; /**< @brief Whether cvc-dir was given. */ 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 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. */ unsigned int read_dg4_given ; /**< @brief Whether read-dg4 was given. */ unsigned int read_dg5_given ; /**< @brief Whether read-dg5 was given. */ unsigned int read_dg6_given ; /**< @brief Whether read-dg6 was given. */ unsigned int read_dg7_given ; /**< @brief Whether read-dg7 was given. */ unsigned int read_dg8_given ; /**< @brief Whether read-dg8 was given. */ unsigned int read_dg9_given ; /**< @brief Whether read-dg9 was given. */ unsigned int read_dg10_given ; /**< @brief Whether read-dg10 was given. */ unsigned int read_dg11_given ; /**< @brief Whether read-dg11 was given. */ unsigned int read_dg12_given ; /**< @brief Whether read-dg12 was given. */ unsigned int read_dg13_given ; /**< @brief Whether read-dg13 was given. */ unsigned int read_dg14_given ; /**< @brief Whether read-dg14 was given. */ unsigned int read_dg15_given ; /**< @brief Whether read-dg15 was given. */ unsigned int read_dg16_given ; /**< @brief Whether read-dg16 was given. */ unsigned int read_dg17_given ; /**< @brief Whether read-dg17 was given. */ unsigned int read_dg18_given ; /**< @brief Whether read-dg18 was given. */ unsigned int read_dg19_given ; /**< @brief Whether read-dg19 was given. */ unsigned int read_dg20_given ; /**< @brief Whether read-dg20 was given. */ unsigned int read_dg21_given ; /**< @brief Whether read-dg21 was given. */ unsigned int write_dg17_given ; /**< @brief Whether write-dg17 was given. */ unsigned int write_dg18_given ; /**< @brief Whether write-dg18 was given. */ unsigned int write_dg19_given ; /**< @brief Whether write-dg19 was given. */ unsigned int write_dg20_given ; /**< @brief Whether write-dg20 was given. */ unsigned int write_dg21_given ; /**< @brief Whether write-dg21 was given. */ unsigned int verify_validity_given ; /**< @brief Whether verify-validity was given. */ unsigned int older_than_given ; /**< @brief Whether older-than was given. */ unsigned int verify_community_given ; /**< @brief Whether verify-community was given. */ 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. */ } ; /** @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 /* NPA_TOOL_CMDLINE_H */ OpenSC-0.26.1/src/tools/npa-tool.c000066400000000000000000000567651474147347300166140ustar00rootroot00000000000000/* * Copyright (C) 2010-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 "npa-tool-cmdline.h" #include "sm/sm-eac.h" #include "util.h" #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_TIME_H #include #else /* only implement what we are using in this file */ struct timeval { unsigned int tv_sec; unsigned int tv_usec; }; int gettimeofday(struct timeval *tv, struct timezone *tz) { #ifdef _WIN32 SYSTEMTIME st; GetLocalTime(&st); if (!tv) return -1; tv->tv_sec = st.wSecond; tv->tv_usec = st.wMilliseconds*1000; #else tv->tv_sec = 0; tv->tv_usec = 0; #endif return 0; } #endif #ifndef HAVE_GETLINE static int getline(char **lineptr, size_t *n, FILE *stream) { char *p; if (!lineptr) return -1; p = realloc(*lineptr, SC_MAX_EXT_APDU_BUFFER_SIZE*3); if (!p) return -1; *lineptr = p; if (fgets(p, SC_MAX_EXT_APDU_BUFFER_SIZE*3, stream) == NULL) return -1; return strlen(p); } #endif /* we don't want to export this from libopensc so we implement it here, again */ #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) typedef ASN1_AUXILIARY_DATA ASN1_AUXILIARY_DATA_NPA_TOOL; /* 0x67 * Auxiliary authenticated 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_NPA_TOOL) IMPLEMENT_ASN1_FUNCTIONS(ASN1_AUXILIARY_DATA_NPA_TOOL) /** * @brief Print binary data to a file stream * * @param[in] file File for printing * @param[in] label Label to prepend to the buffer * @param[in] data Binary data * @param[in] len Length of \a data */ #define bin_print(file, label, data, len) { \ fprintf(file, "%s (%u byte%s)%s%s\n", \ label, (unsigned int) len, len==1?"":"s", len==0?"":":\n", sc_dump_hex(data, len)); \ } 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, "Could not read DG %02u %s (%s)\n", sfid, dg_str, sc_strerror(r)); else { char buf[0x200]; sc_hex_dump(*dg, *dg_len, buf, sizeof buf); fprintf(stdout, "Read %s", buf); } } static void write_dg(sc_card_t *card, unsigned char sfid, const char *dg_str, const char *dg_hex) { unsigned char dg[0xff]; size_t dg_len = sizeof dg; int r; r = sc_hex_to_bin(dg_hex, dg, &dg_len); if (r < 0) { fprintf(stderr, "Could not parse DG %02u %s (%s)\n", sfid, dg_str, sc_strerror(r)); } else { r = iso7816_write_binary_sfid(card, sfid, dg, dg_len); if (r < 0) fprintf(stderr, "Could not write DG %02u %s (%s)\n", sfid, dg_str, sc_strerror(r)); else printf("Wrote DG %02u %s\n", sfid, dg_str); } } #define ISO_VERIFY 0x20 static void verify(sc_card_t *card, const char *verify_str, unsigned char *data, size_t data_len) { sc_apdu_t apdu; int r; sc_format_apdu_ex(&apdu, 0x00, ISO_VERIFY, 0x80, 0, data, data_len, NULL, 0); apdu.cla = 0x80; r = sc_transmit_apdu(card, &apdu); if (r < 0) fprintf(stderr, "Could not verify %s (%s)\n", verify_str, sc_strerror(r)); else printf("Verified %s\n", verify_str); } int npa_translate_apdus(sc_card_t *card, FILE *input) { u8 buf[4 + 3 + 0xffff + 3]; char *read = NULL; size_t readlen = 0, apdulen; sc_apdu_t apdu; int linelen; int r; memset(&apdu, 0, sizeof apdu); while (1) { if (input == stdin) printf("Enter unencrypted C-APDU (empty line to exit)\n"); linelen = getline(&read, &readlen, input); if (linelen <= 1) { if (linelen < 0) { r = SC_ERROR_INTERNAL; sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL, "Could not read line"); } else { r = SC_SUCCESS; printf("Thanks for flying with ccid\n"); } break; } read[linelen - 1] = 0; apdulen = sizeof buf; if (sc_hex_to_bin(read, buf, &apdulen) < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL, "Could not format binary string"); continue; } if (input != stdin) bin_print(stdout, "Unencrypted C-APDU", buf, apdulen); r = sc_bytes2apdu(card->ctx, buf, apdulen, &apdu); if (r < 0) { sc_log_hex(card->ctx, "Invalid C-APDU", buf, apdulen); continue; } apdu.resp = buf; apdu.resplen = sizeof buf; r = sc_transmit_apdu(card, &apdu); if (r < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL, "Could not send C-APDU: %s", sc_strerror(r)); continue; } printf("Decrypted R-APDU sw1=%02x sw2=%02x\n", apdu.sw1, apdu.sw2); bin_print(stdout, "Decrypted R-APDU response data", apdu.resp, apdu.resplen); printf("======================================================================\n"); } if (read) free(read); return r; } 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; CVC_DISCRETIONARY_DATA_TEMPLATE *template = NULL; if (!auxiliary_data) { r = SC_ERROR_INVALID_ARGUMENTS; goto err; } if (!*auxiliary_data) { *auxiliary_data = ASN1_AUXILIARY_DATA_NPA_TOOL_new(); if (!*auxiliary_data) { r = SC_ERROR_INTERNAL; goto err; } } template = CVC_DISCRETIONARY_DATA_TEMPLATE_new(); if (!template) { r = SC_ERROR_INTERNAL; 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; } if (data && data_len) { template->discretionary_data3 = ASN1_OCTET_STRING_new(); if (!template->discretionary_data3 || !ASN1_OCTET_STRING_set( template->discretionary_data3, data, data_len)) { r = SC_ERROR_INTERNAL; goto err; } } if (!sk_push((_STACK*) (*auxiliary_data), template)) { r = SC_ERROR_INTERNAL; goto err; } r = SC_SUCCESS; err: return r; } int main (int argc, char **argv) { const char *newpin = NULL; const char *pin = NULL; const char *puk = NULL; const char *can = NULL; const char *mrz = NULL; unsigned char chat[0xff]; unsigned char desc[0xffff]; unsigned char **certs = NULL; size_t *certs_lens = NULL; unsigned char *privkey = NULL; size_t privkey_len = 0; unsigned char auxiliary_data[0xff]; size_t auxiliary_data_len = 0; unsigned char community_id[0xf]; size_t community_id_len = 0; sc_context_t *ctx = NULL; sc_card_t *card = NULL; sc_context_param_t ctx_param; int r, tr_version = EAC_TR_VERSION_2_02; struct establish_pace_channel_input pace_input; struct establish_pace_channel_output pace_output; struct timeval tv; size_t i; FILE *input = NULL; CVC_CERT *cvc_cert = NULL; unsigned char *certs_chat = NULL; unsigned char *dg = NULL; size_t dg_len = 0; ASN1_AUXILIARY_DATA_NPA_TOOL *templates = NULL; unsigned char *ef_cardsecurity = NULL; size_t ef_cardsecurity_len = 0; struct gengetopt_args_info cmdline; memset(&pace_input, 0, sizeof pace_input); memset(&pace_output, 0, sizeof pace_output); /* Parse command line */ if (cmdline_parser (argc, argv, &cmdline) != 0) exit(1); if (cmdline.env_flag) { can = getenv("CAN"); mrz = getenv("MRZ"); 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; } if (cmdline.chat_given) { pace_input.chat = chat; pace_input.chat_length = sizeof chat; if (sc_hex_to_bin(cmdline.chat_arg, (u8 *) pace_input.chat, &pace_input.chat_length) < 0) { fprintf(stderr, "Could not parse CHAT.\n"); exit(2); } } if (cmdline.cert_desc_given) { pace_input.certificate_description = desc; pace_input.certificate_description_length = sizeof desc; if (sc_hex_to_bin(cmdline.cert_desc_arg, (u8 *) pace_input.certificate_description, &pace_input.certificate_description_length) < 0) { fprintf(stderr, "Could not parse certificate description.\n"); exit(2); } } if (cmdline.tr_03110v201_flag) tr_version = EAC_TR_VERSION_2_01; if (cmdline.disable_ta_checks_flag) eac_default_flags |= EAC_FLAG_DISABLE_CHECK_TA; if (cmdline.disable_ca_checks_flag) eac_default_flags |= EAC_FLAG_DISABLE_CHECK_CA; 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; 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.x509_dir_arg); if (cmdline.break_flag) { /* The biggest number sprintf could write with "%llu is 18446744073709551615 */ char secretbuf[21]; unsigned long long secret = 0; unsigned long long maxsecret = 0; if (cmdline.pin_given) { pace_input.pin_id = PACE_PIN; pace_input.pin_length = 6; maxsecret = 999999; if (pin) { if (sscanf(pin, "%llu", &secret) != 1) { fprintf(stderr, "%s is not an unsigned long long.\n", 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", eac_secret_name(pace_input.pin_id), (unsigned int) pace_input.pin_length); exit(2); } } } else if (cmdline.can_given) { pace_input.pin_id = PACE_CAN; pace_input.pin_length = 6; maxsecret = 999999; if (can) { if (sscanf(can, "%llu", &secret) != 1) { fprintf(stderr, "%s is not an unsigned long long.\n", 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", eac_secret_name(pace_input.pin_id), (unsigned int) pace_input.pin_length); exit(2); } } } else if (cmdline.puk_given) { pace_input.pin_id = PACE_PUK; pace_input.pin_length = 10; maxsecret = 9999999999; if (puk) { if (sscanf(puk, "%llu", &secret) != 1) { fprintf(stderr, "%s is not an unsigned long long.\n", 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", eac_secret_name(pace_input.pin_id), (unsigned int) pace_input.pin_length); exit(2); } } } else { fprintf(stderr, "Please specify whether to do PACE with " "PIN, CAN or PUK.\n"); exit(1); } pace_input.pin = (unsigned char *) secretbuf; do { sprintf(secretbuf, "%0*llu", (unsigned int) pace_input.pin_length, secret); gettimeofday(&tv, NULL); printf("%u,%06u: Trying %s=%s\n", (unsigned int) tv.tv_sec, (unsigned int) tv.tv_usec, eac_secret_name(pace_input.pin_id), pace_input.pin); r = perform_pace(card, pace_input, &pace_output, tr_version); secret++; } while (0 > r && secret <= maxsecret); gettimeofday(&tv, NULL); if (0 > r) { printf("%u,%06u: Tried breaking %s without success.\n", (unsigned int) tv.tv_sec, (unsigned int) tv.tv_usec, 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, eac_secret_name(pace_input.pin_id), pace_input.pin); } } if (cmdline.resume_flag) { pace_input.pin_id = PACE_CAN; if (can) { pace_input.pin = (unsigned char *) can; pace_input.pin_length = strlen(can); } else { pace_input.pin = NULL; pace_input.pin_length = 0; } r = perform_pace(card, pace_input, &pace_output, tr_version); if (r < 0) goto err; printf("Established PACE channel with CAN.\n"); pace_input.pin_id = PACE_PIN; if (pin) { pace_input.pin = (unsigned char *) pin; pace_input.pin_length = strlen(pin); } else { pace_input.pin = NULL; pace_input.pin_length = 0; } r = perform_pace(card, pace_input, &pace_output, tr_version); if (r < 0) goto err; printf("Established PACE channel with PIN. PIN resumed.\n"); } if (cmdline.unblock_flag) { pace_input.pin_id = PACE_PUK; if (puk) { pace_input.pin = (unsigned char *) puk; pace_input.pin_length = strlen(puk); } else { pace_input.pin = NULL; pace_input.pin_length = 0; } r = perform_pace(card, pace_input, &pace_output, tr_version); if (r < 0) goto err; printf("Established PACE channel with PUK.\n"); r = npa_unblock_pin(card); if (r < 0) goto err; printf("Unblocked PIN.\n"); } if (cmdline.new_pin_given) { pace_input.pin_id = PACE_PIN; if (pin) { pace_input.pin = (unsigned char *) pin; pace_input.pin_length = strlen(pin); } else { pace_input.pin = NULL; pace_input.pin_length = 0; } r = perform_pace(card, pace_input, &pace_output, tr_version); if (r < 0) goto err; printf("Established PACE channel with PIN.\n"); r = npa_change_pin(card, newpin, newpin ? strlen(newpin) : 0); if (r < 0) goto err; printf("Changed PIN.\n"); } if (cmdline.translate_given || (!cmdline.resume_flag && !cmdline.new_pin_given && !cmdline.unblock_flag && !cmdline.break_given)) { if (cmdline.cv_certificate_given || cmdline.private_key_given || cmdline.auxiliary_data_given) { if (!cmdline.cv_certificate_given || !cmdline.private_key_given) { fprintf(stderr, "Need at least the terminal's certificate " "and its private key to perform terminal authentication.\n"); exit(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; } for (i = 0; i < cmdline.cv_certificate_given; i++) { if (!fread_to_eof(cmdline.cv_certificate_arg[i], (unsigned char **) &certs[i], &certs_lens[i])) { fprintf(stderr, "Could not read certificate.\n"); r = SC_ERROR_INVALID_DATA; goto err; } } if (!pace_input.chat_length) { const unsigned char *p = certs[cmdline.cv_certificate_given-1]; if (!CVC_d2i_CVC_CERT(&cvc_cert, &p, certs_lens[cmdline.cv_certificate_given-1]) || !cvc_cert || !cvc_cert->body || !cvc_cert->body->certificate_authority_reference || !cvc_cert->body->chat) { fprintf(stderr, "Could not parse certificate.\n"); 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; goto err; } pace_input.chat = certs_chat; } if (!fread_to_eof(cmdline.private_key_arg, &privkey, &privkey_len)) { fprintf(stderr, "Could not parse private key.\n"); r = SC_ERROR_INVALID_DATA; goto err; } if (cmdline.auxiliary_data_given) { auxiliary_data_len = sizeof auxiliary_data; if (sc_hex_to_bin(cmdline.auxiliary_data_arg, auxiliary_data, &auxiliary_data_len) < 0) { fprintf(stderr, "Could not parse auxiliary data.\n"); r = SC_ERROR_INVALID_DATA; goto err; } } else { if (cmdline.older_than_given) { r = add_to_ASN1_AUXILIARY_DATA_NPA_TOOL(&templates, NID_id_DateOfBirth, (unsigned char *) cmdline.older_than_arg, strlen(cmdline.older_than_arg)); if (r < 0) goto err; } if (cmdline.verify_validity_given) { r = add_to_ASN1_AUXILIARY_DATA_NPA_TOOL(&templates, NID_id_DateOfExpiry, (unsigned char *) cmdline.verify_validity_arg, strlen(cmdline.verify_validity_arg)); if (r < 0) goto err; } if (cmdline.verify_community_given) { community_id_len = sizeof community_id; if (sc_hex_to_bin(cmdline.verify_community_arg, community_id, &community_id_len) < 0) { fprintf(stderr, "Could not parse community ID.\n"); exit(2); } r = add_to_ASN1_AUXILIARY_DATA_NPA_TOOL(&templates, NID_id_CommunityID, community_id, community_id_len); if (r < 0) goto err; } if (templates) { unsigned char *p = NULL; auxiliary_data_len = i2d_ASN1_AUXILIARY_DATA_NPA_TOOL( templates, &p); if (0 > (int) auxiliary_data_len || auxiliary_data_len > sizeof auxiliary_data) { free(p); fprintf(stderr, "Auxiliary data too big.\n"); r = SC_ERROR_OUT_OF_MEMORY; goto err; } memcpy(auxiliary_data, p, auxiliary_data_len); free(p); } } } pace_input.pin = NULL; pace_input.pin_length = 0; if (cmdline.pin_given) { pace_input.pin_id = PACE_PIN; if (pin) { pace_input.pin = (unsigned char *) pin; pace_input.pin_length = strlen(pin); } } else if (cmdline.can_given) { pace_input.pin_id = PACE_CAN; if (can) { pace_input.pin = (unsigned char *) can; pace_input.pin_length = strlen(can); } } else if (cmdline.mrz_given) { pace_input.pin_id = PACE_MRZ; if (mrz) { pace_input.pin = (unsigned char *) mrz; pace_input.pin_length = strlen(mrz); } } else if (cmdline.puk_given) { pace_input.pin_id = PACE_PUK; if (puk) { pace_input.pin = (unsigned char *) puk; pace_input.pin_length = strlen(puk); } } else { fprintf(stderr, "Skipping PIN verification\n"); goto nopace; } r = perform_pace(card, pace_input, &pace_output, tr_version); if (r < 0) goto err; printf("Established PACE channel with %s.\n", 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); if (r < 0) goto err; printf("Performed Terminal Authentication.\n"); r = perform_chip_authentication(card, &ef_cardsecurity, &ef_cardsecurity_len); 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); if (cmdline.older_than_given) { unsigned char id_DateOfBirth[] = {6, 9, 4, 0, 127, 0, 7, 3, 1, 4, 1}; verify(card, "age", id_DateOfBirth, sizeof id_DateOfBirth); } if (cmdline.verify_validity_given) { unsigned char id_DateOfExpiry[] = {6, 9, 4, 0, 127, 0, 7, 3, 1, 4, 2}; verify(card, "validity", id_DateOfExpiry, sizeof id_DateOfExpiry); } if (cmdline.verify_community_given) { unsigned char id_CommunityID[] = {6, 9, 4, 0, 127, 0, 7, 3, 1, 4, 3}; verify(card, "community ID", id_CommunityID, sizeof id_CommunityID); } if (cmdline.translate_given) { if (strncmp(cmdline.translate_arg, "stdin", strlen("stdin")) == 0) input = stdin; else { input = fopen(cmdline.translate_arg, "r"); if (!input) { perror("Opening file with APDUs"); r = SC_ERROR_INVALID_DATA; goto err; } } r = npa_translate_apdus(card, input); if (r < 0) goto err; fclose(input); input = NULL; } } err: cmdline_parser_free(&cmdline); 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 (ef_cardsecurity) { OPENSSL_cleanse(ef_cardsecurity, ef_cardsecurity_len); free(ef_cardsecurity); } if (input) fclose(input); if (certs) { i = 0; while (certs[i]) { free((unsigned char *) certs[i]); i++; } free(certs); } free(certs_lens); free(certs_chat); if (cvc_cert) CVC_CERT_free(cvc_cert); free(privkey); free(dg); if (templates) ASN1_AUXILIARY_DATA_NPA_TOOL_free(templates); sc_sm_stop(card); sc_reset(card, 1); sc_disconnect_card(card); sc_release_context(ctx); EAC_cleanup(); if (r < 0) fprintf(stderr, "Error: %s\n", sc_strerror(r)); return -r; } OpenSC-0.26.1/src/tools/npa-tool.ggo.in000066400000000000000000000131741474147347300175360ustar00rootroot00000000000000package "npa-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 "Password Authenticated Connection Establishment (PACE)" option "pin" p "Run PACE with (transport) eID-PIN" string argoptional optional option "puk" u "Run PACE with PUK" string argoptional optional option "can" c "Run PACE with CAN" string argoptional optional option "mrz" m "Run PACE with MRZ (insert MRZ without newlines)" string argoptional optional option "env" - "Whether to use environment variables PIN, PUK, CAN, MRZ and NEWPIN. You may want to clean your environment before enabling this." flag off section "PIN management" option "new-pin" N "Install a new PIN" string argoptional optional option "resume" R "Resume eID-PIN (uses CAN to activate last retry)" flag off option "unblock" U "Unblock PIN (uses PUK to activate three more retries)" flag off section "Terminal Authentication (TA) and Chip Authentication (CA)" option "cv-certificate" C "Card Verifiable Certificate to create a certificate chain. Can be used multiple times (order is important)." string typestr="FILENAME" optional multiple option "cert-desc" - "Certificate description to show for Terminal Authentication" string typestr="HEX_STRING" optional option "chat" - "Card holder authorization template to use (default is terminal's CHAT). Use 7F4C0E060904007F000703010203530103 to trigger EAC on the CAT-C (Komfortleser)." string typestr="HEX_STRING" optional option "auxiliary-data" A "Terminal's auxiliary data (default is determined by verification of validity, age and community ID)." string typestr="HEX_STRING" optional option "private-key" P "Terminal's private key" string typestr="FILENAME" optional option "cvc-dir" - "Where to look for the CVCA's certificate" string typestr="DIRECTORY" default="@CVCDIR@" optional option "x509-dir" - "Where to look for the CSCA's certificate" string typestr="DIRECTORY" default="@X509DIR@" optional option "disable-ta-checks" - "Disable checking the validity period of CV certificates" flag off option "disable-ca-checks" - "Disable passive authentication" flag off section "Read and write data groups" option "read-dg1" - "Read DG 1 (Document Type)" flag off option "read-dg2" - "Read DG 2 (Issuing State)" flag off option "read-dg3" - "Read DG 3 (Date of Expiry)" flag off option "read-dg4" - "Read DG 4 (Given Names)" flag off option "read-dg5" - "Read DG 5 (Family Names)" flag off option "read-dg6" - "Read DG 6 (Religious/Artistic Name)" flag off option "read-dg7" - "Read DG 7 (Academic Title)" flag off option "read-dg8" - "Read DG 8 (Date of Birth)" flag off option "read-dg9" - "Read DG 9 (Place of Birth)" flag off option "read-dg10" - "Read DG 10 (Nationality)" flag off option "read-dg11" - "Read DG 11 (Sex)" flag off option "read-dg12" - "Read DG 12 (Optional Data)" flag off option "read-dg13" - "Read DG 13 (Birth Name)" flag off option "read-dg14" - "Read DG 14" flag off option "read-dg15" - "Read DG 15" flag off option "read-dg16" - "Read DG 16" flag off option "read-dg17" - "Read DG 17 (Normal Place of Residence)" flag off option "read-dg18" - "Read DG 18 (Community ID)" flag off option "read-dg19" - "Read DG 19 (Residence Permit I)" flag off option "read-dg20" - "Read DG 20 (Residence Permit II)" flag off option "read-dg21" - "Read DG 21 (Optional Data)" flag off option "write-dg17" - "Write DG 17 (Normal Place of Residence)" string typestr="HEX_STRING" optional option "write-dg18" - "Write DG 18 (Community ID)" string typestr="HEX_STRING" optional option "write-dg19" - "Write DG 19 (Residence Permit I)" string typestr="HEX_STRING" optional option "write-dg20" - "Write DG 20 (Residence Permit II)" string typestr="HEX_STRING" optional option "write-dg21" - "Write DG 21 (Optional Data)" string typestr="HEX_STRING" optional section "Verification of validity, age and community ID" option "verify-validity" - "Verify chip's validity with a reference date" string typestr="YYYYMMDD" optional option "older-than" - "Verify age with a reference date" string typestr="YYYYMMDD" optional option "verify-community" - "Verify community ID with a reference ID" string typestr="HEX_STRING" optional section "Special options, not always useful" option "break" b "Brute force PIN, CAN or PUK. Use together with -p, -a or -u" flag off option "translate" t "File with APDUs of HEX_STRINGs to send through the secure channel" string typestr="FILENAME" default="stdin" optional option "tr-03110v201" - "Force compliance to BSI TR-03110 version 2.01" flag off text " Report bugs to @PACKAGE_BUGREPORT@ Written by Frank Morgner " OpenSC-0.26.1/src/tools/openpgp-tool-helpers.c000066400000000000000000000133171474147347300211300ustar00rootroot00000000000000/* * 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; } OpenSC-0.26.1/src/tools/openpgp-tool-helpers.h000066400000000000000000000027431474147347300211360ustar00rootroot00000000000000/* * 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 */ OpenSC-0.26.1/src/tools/openpgp-tool.c000066400000000000000000000532361474147347300174740ustar00rootroot00000000000000/* * openpgp-tool.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 /* For dup() and dup2() functions */ #ifndef _WIN32 #include #else /* * Windows: * https://msdn.microsoft.com/en-us/library/8syseb29.aspx * https://msdn.microsoft.com/en-us/library/886kc0as.aspx */ #include #include #endif #include #include #include #include #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 #define OPT_VERIFY 258 #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; 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 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_info(sc_card_t *card, const struct ef_name_map *map); /* define global variables */ static int actions = 0; static char *opt_reader = NULL; static int opt_wait = 0; 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 u8 key_id = 0; 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 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"; static const struct option options[] = { { "reader", required_argument, NULL, 'r' }, { "wait", no_argument, NULL, 'w' }, { "exec", required_argument, NULL, 'x' }, { "raw", no_argument, NULL, OPT_RAW }, { "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-type", required_argument, NULL, 't' }, { "help", no_argument, NULL, 'h' }, { "verbose", no_argument, NULL, 'v' }, { "version", no_argument, NULL, 'V' }, { "erase", no_argument, NULL, 'E' }, { "verify", required_argument, NULL, OPT_VERIFY }, { "pin", required_argument, NULL, OPT_PIN }, { "del-key", required_argument, NULL, OPT_DELKEY }, { "do", required_argument, NULL, 'd' }, { NULL, 0, NULL, 0 } }; static const char *option_help[] = { /* r */ "Use reader number [0]", /* w */ "Wait for card insertion", /* x */ "Execute program with data in env vars", "Print values in raw format", "Print values in pretty format", /* C */ "Show card information", /* U */ "Show card holder information", /* K */ "Show key information", /* G */ "Generate key", /* t */ "Key type (default: rsa2048)", /* h */ "Print this help message", /* 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. 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 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 } }; static void show_version(void) { fprintf(stderr, "openpgp-tool - OpenPGP card utility version " PACKAGE_VERSION "\n" "\n" "Copyright (c) 2012-2020 Peter Marschall \n" "Licensed under LGPL v2\n"); } #define INDENT 16 static void display_data(const struct ef_name_map *map, u8 *data, size_t length) { 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; } } 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; } } if (value != NULL) { if (exec_program) { char *envvar= malloc(strlen(map->env_name) + strlen(value) + 2); if (envvar != NULL) { strcpy(envvar, map->env_name); strcat(envvar, "="); strcat(envvar, value); putenv(envvar); /* envvar deliberately kept: see putenv(3) */ } } else { const char *label = map->name; int fill = (int) (INDENT - strlen(label)); printf("%s:%*s%s\n", label, fill, "", 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:KEht:wvVd:", options, (int *) 0)) != -1) { switch (c) { case 'r': opt_reader = optarg; break; case 'x': if (exec_program) free(exec_program); exec_program = strdup(optarg); break; case OPT_RAW: opt_raw = 1; break; case OPT_PRETTY: opt_raw = 0; break; case OPT_VERIFY: opt_verify++; if (verifytype) free(verifytype); verifytype = strdup(optarg); actions++; break; case OPT_PIN: opt_pin++; util_get_pin(optarg, &pin); break; case 'C': opt_cardinfo++; actions++; break; case 'U': opt_userinfo++; actions++; break; case 'K': opt_keyinfo++; actions++; break; case 'G': opt_genkey++; key_id = optarg[0] - '0'; actions++; break; 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); break; case 'w': opt_wait = 1; break; case 'v': verbose++; break; case 'V': show_version(); exit(EXIT_SUCCESS); break; case 'E': opt_erase++; actions++; break; case OPT_DELKEY: opt_delkey++; if (strcmp(optarg, "all") != 0) /* Arg string is not 'all' */ key_id = optarg[0] - '0'; else /* Arg string is 'all' */ key_id = 'a'; actions++; break; case 'd': 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: util_print_usage_and_die(app_name, options, option_help, NULL); } } return optind; } static int do_info(sc_card_t *card, const struct ef_name_map *map) { int i; u8 buf[2048]; for (i = 0; map[i].ef != NULL; i++) { sc_path_t path; sc_file_t *file; size_t count; int r; sc_format_path(map[i].ef, &path); r = sc_select_file(card, &path, &file); if (r) { util_error("failed to select EF %s: %s", map[i].ef, sc_strerror(r)); return EXIT_FAILURE; } count = file->size; if (!count) continue; if (count > sizeof(buf) - 1) { 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) { util_error("failed to read %s: %s", map[i].ef, sc_strerror(r)); return EXIT_FAILURE; } 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'; display_data(&map[i], buf, count); } return EXIT_SUCCESS; } static int do_dump_do(sc_card_t *card, unsigned int tag) { struct pgp_priv_data *priv = DRVDATA(card); int r; size_t length; unsigned char *buffer; if (tag < 0x101 || tag > 0x104) { util_error("illegal DO identifier %04X", tag); return SC_ERROR_INVALID_ARGUMENTS; } 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) { 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) { int tmp; FILE *fp; #ifndef _WIN32 tmp = dup(fileno(stdout)); #else tmp = _dup(_fileno(stdout)); #endif if (tmp < 0) return EXIT_FAILURE; fp = freopen(NULL, "wb", stdout); if (fp) { r = (int) fwrite(buffer, sizeof(char), length, fp); } #ifndef _WIN32 dup2(tmp, fileno(stdout)); #else _dup2(tmp, _fileno(stdout)); #endif clearerr(stdout); close(tmp); if (length != (size_t) r) { /* fail on write errors */ free(buffer); return EXIT_FAILURE; } } else { util_hex_dump_asc(stdout, buffer, length, -1); } free(buffer); return EXIT_SUCCESS; } int do_genkey(sc_card_t *card, u8 in_key_id, const char *keytype) { int r; sc_cardctl_openpgp_keygen_info_t key_info; u8 fingerprints[60]; sc_path_t path; sc_file_t *file; /* 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; } /* fall back to RSA 2048 if keytype is not given */ if (!keytype) keytype = "RSA2048"; memset(&key_info, 0, sizeof(sc_cardctl_openpgp_keygen_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) { util_error("failed to retrieve fingerprints: %s", sc_strerror(r)); return EXIT_FAILURE; } 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 *in_pin) { struct sc_pin_cmd_data data; int tries_left; int r; if (!type || !in_pin) return SC_ERROR_INVALID_ARGUMENTS; if (strncasecmp("CHV", type, 3) != 0) { 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') { util_error("invalid PIN reference. Please use CHV1, CHV2 or CHV3"); return SC_ERROR_INVALID_PIN_REFERENCE; } memset(&data, 0, sizeof(struct sc_pin_cmd_data)); data.cmd = SC_PIN_CMD_VERIFY; data.pin_type = SC_AC_CHV; data.pin_reference = type[3] - '0'; data.pin1.data = (unsigned char *) in_pin; data.pin1.len = (int)strlen(in_pin); r = sc_pin_cmd(card, &data, &tries_left); return r; } /** * 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 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"; /* We need to replace the 4th byte later */ char *apdustring = NULL; u8 buf[SC_MAX_APDU_BUFFER_SIZE]; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; sc_apdu_t apdu; size_t len0; int i; int r = SC_SUCCESS; for (i = 0; i < 2; i++) { if (i == 0) /* Reset fingerprint */ apdustring = del_fingerprint; else /* Reset creation time */ apdustring = del_creationtime; /* Convert the string to binary array */ len0 = sizeof(buf); sc_hex_to_bin(apdustring, buf, &len0); /* Replace DO tag, subject to key ID */ buf[3] = buf[3] + in_key_id; /* Build APDU from binary array */ r = sc_bytes2apdu(card->ctx, buf, len0, &apdu); if (r) { util_error("failed to build APDU: %s", sc_strerror(r)); return r; } apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); /* Send APDU to card */ r = sc_transmit_apdu(card, &apdu); if (r) { util_error("transmitting APDU failed: %s", sc_strerror(r)); return r; } } /* TODO: Rewrite Extended Header List. * Not support by OpenPGP v2 yet */ return r; } 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) { util_error("only Gnuk supports deleting keys. General OpenPGP doesn't"); return SC_ERROR_NOT_SUPPORTED; } 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 (in_key_id == 1 || in_key_id == 'a') { sc_format_path("B601", &path); r |= sc_delete_file(card, &path); } if (in_key_id == 2 || in_key_id == 'a') { sc_format_path("B801", &path); r |= sc_delete_file(card, &path); } if (in_key_id == 3 || in_key_id == 'a') { sc_format_path("A401", &path); r |= sc_delete_file(card, &path); } return r; } int do_erase(sc_card_t *card) { printf("Erase card\n"); return sc_card_ctl(card, SC_CARDCTL_ERASE_CARD, NULL); } int main(int argc, char **argv) { sc_context_t *ctx = NULL; sc_context_param_t ctx_param; sc_card_t *card = NULL; int r; int argind = 0; int exit_status = EXIT_SUCCESS; /* decode options */ argind = decode_options(argc, argv); /* connect to the card */ 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", sc_strerror(r)); return EXIT_FAILURE; } /* 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); if (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_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; } /* fail on too many arguments */ if (argind > argc) util_print_usage_and_die(app_name, options, option_help, NULL); /* set default action */ if (!actions) opt_userinfo = 1; if (opt_cardinfo) exit_status |= do_info(card, card_data); if (opt_userinfo) 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) { 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, opt_keytype); if (exec_program) { char *const largv[] = {exec_program, NULL}; sc_unlock(card); sc_disconnect_card(card); sc_release_context(ctx); #ifndef _WIN32 execv(exec_program, largv); #else _execv(exec_program, (const char * const*)largv); #endif /* we should not get here */ perror("execv()"); exit(EXIT_FAILURE); } if (opt_delkey) exit_status |= do_delete_key(card, key_id); if (opt_erase) exit_status |= do_erase(card); out: sc_unlock(card); sc_disconnect_card(card); sc_release_context(ctx); exit(exit_status); } OpenSC-0.26.1/src/tools/opensc-asn1-cmdline.c000066400000000000000000000227571474147347300206150ustar00rootroot00000000000000/* 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 : */ OpenSC-0.26.1/src/tools/opensc-asn1-cmdline.h000066400000000000000000000142361474147347300206130ustar00rootroot00000000000000/** @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 */ OpenSC-0.26.1/src/tools/opensc-asn1.c000066400000000000000000000027531474147347300171760ustar00rootroot00000000000000/* * 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; } OpenSC-0.26.1/src/tools/opensc-asn1.ggo.in000066400000000000000000000002611474147347300201250ustar00rootroot00000000000000package "opensc-asn1" purpose "@PACKAGE_SUMMARY@" description "Parse ASN.1 data." text " Report bugs to @PACKAGE_BUGREPORT@ Written by Frank Morgner " OpenSC-0.26.1/src/tools/opensc-explorer.c000066400000000000000000001643761474147347300202060ustar00rootroot00000000000000/* * opensc-explorer.c: A shell for accessing smart cards with libopensc * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include #include #include #include #include #ifdef ENABLE_READLINE #include #include #endif #if !defined(_WIN32) #include /* for htons() */ #endif #ifdef _WIN32 #include /* for_setmode() */ #include /* for _O_TEXT and _O_BINARY */ #endif #ifdef HAVE_IO_H #include #endif #include "libopensc/opensc.h" #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 #include "util.h" #define DIM(v) (sizeof(v)/sizeof((v)[0])) /* type for associations of IDs to names */ typedef struct _id2str { unsigned int id; const char *str; } id2str_t; static const char *app_name = "opensc-explorer"; static int opt_wait = 0, verbose = 0; static const char *opt_driver = NULL; static const char *opt_reader = NULL; static const char *opt_startfile = NULL; 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' }, { "card-driver", 1, NULL, 'c' }, { "mf", 1, NULL, 'm' }, { "wait", 0, NULL, 'w' }, { "verbose", 0, NULL, 'v' }, { NULL, 0, NULL, 0 } }; static const char *option_help[] = { "Uses reader number [0]", "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, may be used several times", }; /* declare functions called by user commands */ static int do_echo(int argc, char **argv); static int do_ls(int argc, char **argv); static int do_find(int argc, char **argv); static int do_find_tags(int argc, char **argv); static int do_cd(int argc, char **argv); static int do_cat(int argc, char **argv); 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); static int do_debug(int argc, char **argv); static int do_erase(int argc, char **argv); static int do_random(int argc, char **argv); static int do_get_data(int argc, char **argv); static int do_put_data(int argc, char **argv); static int do_apdu(int argc, char **argv); static int do_sm(int argc, char **argv); static int do_asn1(int argc, char **argv); static int do_help(int argc, char **argv); static int do_quit(int argc, char **argv); struct command { int (*func)(int, char **); const char * name; const char * args; const char * help; }; static struct command cmds[] = { { do_echo, "echo", "[ ...]", "display arguments" }, { do_ls, "ls", "[ ...]", "list files in the current DF" }, { do_find, "find", "[ []]", "find all files in the current DF" }, { do_find_tags, "find_tags", "[ []]", "find all tags of data objects in the current context" }, { do_cd, "cd", "{.. | | aid:}", "change to another DF" }, { do_cat, "cat", "[{ | sfi:} []]" , "print the contents of [a record in] an EF" }, { do_info, "info", "[]", "display attributes of card file" }, { do_create, "create", " ", "create a new EF" }, { do_mkdir, "mkdir", " ", "create a new DF" }, { do_delete, "delete", "", "remove an EF/DF" }, { do_delete, "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} []", "present a PIN or key to the card" }, { do_change, "change", "CHV [[] ]", "change a PIN" }, { do_unblock, "unblock", "CHV [ []]", "unblock a PIN" }, { do_put, "put", " []", "copy a local file to the card" }, { do_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", " []", "get a data object" }, { do_put_data, "do_put", " ", "put a data object" }, { do_erase, "erase", "", "erase card" }, { do_random, "random", " []", "obtain random bytes from card" }, { do_update_record, "update_record", " ", "update record" }, { do_update_binary, "update_binary", " ", "update binary" }, { do_apdu, "apdu", " ...", "send a custom apdu command" }, { do_asn1, "asn1", "[ [] []]", "decode an ASN.1 file or record" }, { do_sm, "sm", "{open|close}", "call SM 'open' or 'close' handlers, if available"}, { do_debug, "debug", "[]", "get/set the debug level" }, { do_quit, "quit", "", "quit this program" }, { do_quit, "exit", "", "quit this program" }, { do_help, "help", "", "show this help" }, { NULL, NULL, NULL, NULL } }; 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+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; } static int parse_string_or_hexdata(const char *in, u8 *out, size_t *outlen) { if (in == NULL) return SC_ERROR_INVALID_ARGUMENTS; if (*in == '"') { u8 quote = *in++; size_t count = 0; while (*in != quote && *in != '\0' && count < *outlen) out[count++] = *in++; if (*in == '\0') return SC_ERROR_INVALID_ARGUMENTS; if (count >= *outlen) return SC_ERROR_BUFFER_TOO_SMALL; *outlen = count; return 0; } else return sc_hex_to_bin(in, out, outlen); } static int usage(int (*func)(int, char **)) { struct command *cmd; for (cmd = cmds; cmd->func; cmd++) if (cmd->func == func) printf("Usage: %s %s\n", cmd->name, cmd->args); return -1; } static void die(int ret) { sc_file_free(current_file); if (card) { sc_disconnect_card(card); } if (ctx) sc_release_context(ctx); exit(ret); } static void select_current_path_or_die(void) { if (current_path.type || current_path.len) { int r = sc_lock(card); if (r == SC_SUCCESS) r = sc_select_file(card, ¤t_path, NULL); sc_unlock(card); if (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, int *ambiguous) { struct command *last_match = NULL; 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; } } } /* indicate as non-ambiguous if there was no match */ if (ambiguous != NULL && last_match == NULL) *ambiguous = 0; return last_match; } static void check_ret(int r, int op, const char *err, const sc_file_t *file) { fprintf(stderr, "%s: %s\n", err, sc_strerror(r)); if (r == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED) fprintf(stderr, "ACL for operation: %s\n", util_acl_to_str(sc_file_get_acl_entry(file, op))); } static int arg_to_fid(const char *arg, u8 *fid) { unsigned int fid0, fid1; if (strlen(arg) != 4) { fprintf(stderr, "Wrong ID length.\n"); return -1; } if (sscanf(arg, "%02X%02X", &fid0, &fid1) != 2) { fprintf(stderr, "Invalid ID.\n"); return -1; } fid[0] = (unsigned char)fid0; fid[1] = (unsigned char)fid1; return 0; } static int arg_to_path(const char *arg, sc_path_t *path, int is_id) { memset(path, 0, sizeof(sc_path_t)); if (strncasecmp(arg, "aid:", strlen("aid:")) == 0) { /* DF aid */ const char *p = arg + strlen("aid:"); int r; path->type = SC_PATH_TYPE_DF_NAME; path->len = sizeof(path->value); if ((r = sc_hex_to_bin(p, path->value, &path->len)) < 0) { fprintf(stderr, "Error parsing AID: %s\n", p); return r; } } else { /* file id */ u8 cbuf[2]; if (arg_to_fid(arg, cbuf) < 0) return -1; if ((cbuf[0] == 0x3F && cbuf[1] == 0x00) || is_id) { path->len = 2; memcpy(path->value, cbuf, 2); 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)) { fprintf(stderr, "Invalid length of DF_NAME path\n"); return -1; } memcpy(path->aid.value, path->value, path->len); path->aid.len = path->len; path->type = SC_PATH_TYPE_FILE_ID; path->len = 0; } sc_append_path_id(path, cbuf, 2); } } return 0; } static void print_file(const sc_file_t *file) { const char *format = " %02X%02X "; const char *st = NULL; if(!file->type_attr_len) st = "???"; switch (file->type) { case SC_FILE_TYPE_WORKING_EF: st = "wEF"; break; case SC_FILE_TYPE_INTERNAL_EF: st = "iEF"; break; case SC_FILE_TYPE_DF: format = "[%02X%02X]"; st = "DF"; break; } printf(format, file->id >> 8, file->id & 0xFF); if (st == NULL) printf("\t0x%02X", *file->type_attr); else printf("\t%4s", st); printf(" %5lu", (unsigned long)file->size); if (file->namelen) { printf("\tName: "); util_print_binary(stdout, file->name, file->namelen); } printf("\n"); return; } static int do_echo(int argc, char **argv) { int i; for (i = 0; i < argc; i++) { printf("%s%s", argv[i], (i < argc) ? " " : ""); } printf("\n"); return 0; } static int pattern_match(const char *pattern, const char *string) { if (pattern == NULL || string == NULL) return 0; while (*pattern != '\0' && *string != '\0') { /* wildcard matching multiple characters */ if (*pattern == '*') { for (pattern++; *string != '\0' ; string++) if (pattern_match(pattern, string)) return 1; return 0; } /* simple character class matching a single character */ else if (*pattern == '[') { char *end = strchr(pattern, ']'); int match = 0; for (pattern++; end != NULL && pattern != end; pattern++) { if (tolower((unsigned char) *pattern) == tolower((unsigned char) *string)) match++; } if (!match) return 0; pattern++; string++; } /* single character comparison / wildcard matching a single character */ else if (tolower((unsigned char) *pattern) == tolower((unsigned char) *string) || *pattern == '?') { pattern++; string++; } else return 0; if (*string == '\0' || *pattern == '\0') break; } 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[SC_MAX_EXT_APDU_RESP_SIZE], *cur = buf; int r, count; 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); return -1; } count = r; printf("FileID\tType Size\n"); while (count >= 2) { char filename[SC_MAX_PATH_STRING_SIZE]; int i = 0; int matches = 0; /* construct file name */ sprintf(filename, "%02X%02X", cur[0], cur[1]); /* compare file name against patterns */ for (i = 0; i < argc; i++) { if (pattern_match(argv[i], filename)) { matches = 1; break; } } /* 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) { fprintf(stderr, "Unable to set path.\n"); die(1); } } r = sc_lock(card); if (r == SC_SUCCESS) r = sc_select_file(card, &path, &file); sc_unlock(card); if (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); sc_file_free(file); } } cur += 2; count -= 2; select_current_path_or_die(); } return 0; } static int do_find(int argc, char **argv) { u8 fid[2] = { 0x00, 0x00 }; u8 end[2] = { 0xFF, 0xFF }; sc_path_t path; int r; switch (argc) { case 2: if (arg_to_fid(argv[1], end) != 0) return usage(do_find); /* fall through */ case 1: if (arg_to_fid(argv[0], fid) != 0) return usage(do_find); /* fall through */ case 0: break; default: return usage(do_find); } printf("FileID\tType Size\n"); while (1) { sc_file_t *file = NULL; printf("(%02X%02X)\r", fid[0], fid[1]); fflush(stdout); if (current_path.type != SC_PATH_TYPE_DF_NAME) { path = current_path; sc_append_path_id(&path, fid, sizeof(fid)); } else { 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_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]; print_file(file); sc_file_free(file); select_current_path_or_die(); break; case SC_ERROR_NOT_ALLOWED: case SC_ERROR_SECURITY_STATUS_NOT_SATISFIED: printf("(%02X%02X)\t%s\n", fid[0], fid[1], sc_strerror(r)); break; } if (fid[0] == end[0] && fid[1] == end[1]) break; fid[1] = fid[1] + 1; if (fid[1] == 0) fid[0] = fid[0] + 1; } return 0; } static int do_find_tags(int argc, char **argv) { 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; switch (argc) { case 2: if (arg_to_fid(argv[1], end) != 0) return usage(do_find_tags); /* fall through */ case 1: if (arg_to_fid(argv[0], start) != 0) return usage(do_find_tags); /* fall through */ case 0: break; default: return usage(do_find_tags); } tag = (start[0] << 8) | start[1]; tag_end = (end[0] << 8) | end[1]; printf("Tag\tType\n"); while (1) { printf("(%04X)\r", tag); fflush(stdout); 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) printf("\tdump file"); if ((0x0001 <= tag && tag <= 0x00FE) || (0x1F1F <= tag && tag <= 0xFFFF)) printf("\tBER-TLV"); if (tag == 0x00FF || tag == 0x02FF) printf("\tspecial function"); if (0x0100 <= tag && tag <= 0x01FF) printf("\tproprietary"); if (tag == 0x0200) printf("\tRFU"); if (0x0201 <= tag && tag <= 0x02FE) printf("\tSIMPLE-TLV"); printf("\n"); if (r > 0) util_hex_dump_asc(stdout, rbuf, r, -1); } else { switch (r) { case SC_ERROR_NOT_ALLOWED: case SC_ERROR_SECURITY_STATUS_NOT_SATISFIED: printf("(%04X)\t%s\n", tag, sc_strerror(r)); break; } } if (tag >= tag_end) break; tag++; } return 0; } static int do_cd(int argc, char **argv) { sc_path_t path; sc_file_t *file; int r; if (argc != 1) return usage(do_cd); if (strcmp(argv[0], "..") == 0) { path = current_path; if (path.len < 4) { fprintf(stderr, "unable to go up, already in MF.\n"); return -1; } if (path.type == SC_PATH_TYPE_DF_NAME) { sc_format_path("3F00", &path); } else { path.len -= 2; } r = sc_lock(card); if (r == SC_SUCCESS) r = sc_select_file(card, &path, &file); sc_unlock(card); if (r) { fprintf(stderr, "unable to go up: %s\n", sc_strerror(r)); return -1; } sc_file_free(current_file); current_file = file; current_path = path; return 0; } if (arg_to_path(argv[0], &path, 0) != 0) return usage(do_cd); 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) && (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; } current_path = path; sc_file_free(current_file); current_file = file; return 0; } static int read_and_print_binary_file(sc_file_t *file) { u8 *buf; size_t size = (file->size > 0) ? file->size : SC_MAX_EXT_APDU_RESP_SIZE; int r, ret = -1; buf = calloc(1, size); if (buf == NULL) 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; } util_hex_dump_asc(stdout, buf, r, 0); ret = 0; err: free(buf); return ret; } static int read_and_print_record_file(sc_file_t *file, unsigned char sfi, unsigned int wanted) { u8 buf[SC_MAX_EXT_APDU_RESP_SIZE]; int r; unsigned int rec; 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); 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 > 2) return usage(do_cat); if (argc > 1) rec = (unsigned int) strtoul(argv[1], NULL, 10); if (!argc) { path = current_path; file = current_file; not_current = 0; } else { const char sfi_prefix[] = "sfi:"; if (strncasecmp(argv[0], sfi_prefix, strlen(sfi_prefix)) == 0) { const char *sfi_n = argv[0] + strlen(sfi_prefix); if(!current_file) { fprintf(stderr, "A DF must be selected to read by SFI\n"); goto err; } path = current_path; file = current_file; not_current = 0; sfi = atoi(sfi_n); if ((sfi < 1) || (sfi > 30)) { 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_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); goto err; } } } if (file->type != SC_FILE_TYPE_WORKING_EF && !(file->type == SC_FILE_TYPE_DF && sfi)) { fprintf(stderr, "only working EFs may be read\n"); goto err; } 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, rec); err = 0; err: if (not_current) { sc_file_free(file); select_current_path_or_die(); } return -err; } static int do_info(int argc, char **argv) { sc_file_t *file; sc_path_t path; size_t i; const char *st = NULL; 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; not_current = 0; } else if (argc == 1) { if (arg_to_path(argv[0], &path, 0) != 0) return usage(do_info); r = sc_lock(card); if (r == SC_SUCCESS) r = sc_select_file(card, &path, &file); sc_unlock(card); if (r) { fprintf(stderr, "unable to select file: %s\n", sc_strerror(r)); return -1; } } else return usage(do_info); 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 = "Internal Elementary File"; break; case SC_FILE_TYPE_DF: st = "Dedicated File"; break; } if (st == NULL) printf("\nFile type [%02x] ID %04X", *file->type_attr, file->id); else printf("\n%s ID %04X", st, file->id); if (file->sid) printf(", SFI %02X", file->sid); 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[] = { { SC_AC_OP_SELECT, "SELECT" }, { SC_AC_OP_LOCK, "LOCK" }, { SC_AC_OP_DELETE, "DELETE" }, { SC_AC_OP_CREATE, "CREATE" }, { SC_AC_OP_REHABILITATE, "REHABILITATE" }, { SC_AC_OP_INVALIDATE, "INVALIDATE" }, { SC_AC_OP_LIST_FILES, "LIST FILES" }, { SC_AC_OP_CRYPTO, "CRYPTO" }, { SC_AC_OP_DELETE_SELF, "DELETE SELF" }, { 0, NULL } }; if (file->namelen) { printf("%-25s", "DF name:"); util_print_binary(stdout, file->name, file->namelen); printf("\n"); } ac_ops = ac_ops_df; } else { static const id2str_t ac_ops_ef[] = { { SC_AC_OP_READ, "READ" }, { SC_AC_OP_UPDATE, "UPDATE" }, { SC_AC_OP_DELETE, "DELETE" }, { SC_AC_OP_WRITE, "WRITE" }, { SC_AC_OP_REHABILITATE, "REHABILITATE" }, { SC_AC_OP_INVALIDATE, "INVALIDATE" }, { SC_AC_OP_LIST_FILES, "LIST FILES" }, { SC_AC_OP_CRYPTO, "CRYPTO" }, { 0, NULL } }; const id2str_t ef_type_name[] = { { SC_FILE_EF_TRANSPARENT, "Transparent" }, { SC_FILE_EF_LINEAR_FIXED, "Linear fixed" }, { SC_FILE_EF_LINEAR_FIXED_TLV, "Linear fixed, SIMPLE-TLV" }, { SC_FILE_EF_LINEAR_VARIABLE, "Linear variable" }, { SC_FILE_EF_LINEAR_VARIABLE_TLV, "Linear variable, SIMPLE-TLV" }, { SC_FILE_EF_CYCLIC, "Cyclic" }, { SC_FILE_EF_CYCLIC_TLV, "Cyclic, SIMPLE-TLV" }, { 0, NULL } }; const char *ef_type = "Unknown"; 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("%-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++) { size_t len = strlen(ac_ops[i].str); printf("ACL for %s:%*s %s\n", ac_ops[i].str, (15 > len) ? (int)(15 - len) : 0, "", util_acl_to_str(sc_file_get_acl_entry(file, ac_ops[i].id))); } 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 > 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); select_current_path_or_die(); } return 0; } static int create_file(sc_file_t *file) { int r; 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; } /* Make sure we're back in the parent directory, because on some cards * CREATE FILE also selects the newly created file. */ select_current_path_or_die(); return 0; } static int do_create(int argc, char **argv) { sc_path_t path; sc_file_t *file; unsigned int size; int r, op; if (argc < 2) return usage(do_create); if (arg_to_path(argv[0], &path, 1) != 0) return usage(do_create); /* %z isn't supported everywhere */ if (sscanf(argv[1], "%u", &size) != 1) return usage(do_create); file = sc_file_new(); file->id = (path.value[0] << 8) | path.value[1]; file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = SC_FILE_EF_TRANSPARENT; file->size = (size_t) size; file->status = SC_FILE_STATUS_ACTIVATED; for (op = 0; op < SC_MAX_AC_OPS; op++) sc_file_add_acl_entry(file, op, SC_AC_NONE, 0); if (argc > 2) { snprintf((char *)file->name, sizeof(file->name), "%s", argv[2]); file->namelen = strlen((char *)file->name); } r = create_file(file); sc_file_free(file); return r; } static int do_mkdir(int argc, char **argv) { sc_path_t path; sc_file_t *file; unsigned int size; int r, op; if (argc != 2) return usage(do_mkdir); if (arg_to_path(argv[0], &path, 1) != 0) return usage(do_mkdir); if (sscanf(argv[1], "%u", &size) != 1) return usage(do_mkdir); file = sc_file_new(); file->id = (path.value[0] << 8) | path.value[1]; file->type = SC_FILE_TYPE_DF; file->size = size; file->status = SC_FILE_STATUS_ACTIVATED; for (op = 0; op < SC_MAX_AC_OPS; op++) sc_file_add_acl_entry(file, op, SC_AC_NONE, 0); r = create_file(file); sc_file_free(file); return r; } static int do_delete(int argc, char **argv) { sc_path_t path; int r; if (argc != 1) return usage(do_delete); if (arg_to_path(argv[0], &path, 1) != 0) return usage(do_delete); if (path.len != 2) return usage(do_delete); path.type = SC_PATH_TYPE_FILE_ID; 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; } 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, tries_left = -1; 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, &tries_left); sc_unlock(card); if (r) { fprintf(stderr, "Unable to get PIN info: %s\n", sc_strerror(r)); return -1; } switch (data.pin1.logged_in) { case SC_PIN_STATE_LOGGED_IN: printf("Logged in.\n"); break; case SC_PIN_STATE_LOGGED_OUT: printf("Logged out.\n"); break; case SC_PIN_STATE_UNKNOWN: default: printf("Login status unknown.\n"); } if (tries_left >= 0) printf("%d tries left.\n", tries_left); return 0; } static int do_verify(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, tries_left = -1; u8 buf[SC_MAX_PIN_SIZE]; size_t buflen = sizeof(buf), i; struct sc_pin_cmd_data data; size_t prefix_len = 0; if (argc < 1 || argc > 2) return usage(do_verify); memset(&data, 0, sizeof(data)); data.cmd = SC_PIN_CMD_VERIFY; 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_verify); } if (sscanf(argv[0] + prefix_len, "%d", &data.pin_reference) != 1) { fprintf(stderr, "Invalid key reference.\n"); return usage(do_verify); } if (argc < 2) { if (card->reader->capabilities & SC_READER_CAP_PIN_PAD) { printf("Please enter PIN on the reader's pin pad.\n"); data.pin1.prompt = "Please enter PIN"; data.flags |= SC_PIN_CMD_USE_PINPAD; } else { char *pin = NULL; size_t len = 0; printf("Please enter PIN: "); r = util_getpass(&pin, &len, stdin); if (r < 0) { fprintf(stderr, "util_getpass error.\n"); return -1; } if (strlcpy((char *)buf, pin, sizeof(buf)) >= sizeof(buf)) { free(pin); fprintf(stderr, "PIN too long - aborting VERIFY.\n"); return -1; } free(pin); data.pin1.data = buf; data.pin1.len = strlen((char *)buf); } } else { r = parse_string_or_hexdata(argv[1], buf, &buflen); if (0 != r) { fprintf(stderr, "Invalid key value.\n"); return usage(do_verify); } data.pin1.data = buf; data.pin1.len = buflen; } r = sc_lock(card); if (r == SC_SUCCESS) r = sc_pin_cmd(card, &data, &tries_left); 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); else printf("Incorrect code.\n"); } else fprintf(stderr, "Unable to verify PIN code: %s\n", sc_strerror(r)); return -1; } printf("Code correct.\n"); return 0; } static int do_change(int argc, char **argv) { int ref, r, tries_left = -1; 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; memset(&data, 0, sizeof(data)); data.cmd = SC_PIN_CMD_CHANGE; if (argc < 1 || argc > 3) return usage(do_change); if (strncasecmp(argv[0], "CHV", 3)) { fprintf(stderr, "Invalid type.\n"); return usage(do_change); } if (sscanf(argv[0] + 3, "%d", &ref) != 1) { 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) { fprintf(stderr, "Invalid key value.\n"); return usage(do_change); } } if (argc >= 2) { newpinlen = sizeof(newpin); if (parse_string_or_hexdata(argv[argc-1], newpin, &newpinlen) != 0) { fprintf(stderr, "Invalid key value.\n"); return usage(do_change); } } data.pin_type = SC_AC_CHV; data.pin_reference = ref; data.pin1.data = oldpinlen ? oldpin : NULL; data.pin1.len = oldpinlen; data.pin2.data = newpinlen ? newpin : NULL; data.pin2.len = newpinlen; r = sc_lock(card); if (r == SC_SUCCESS) r = sc_pin_cmd(card, &data, &tries_left); 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); else printf("Incorrect code.\n"); } fprintf(stderr, "Unable to change PIN code: %s\n", sc_strerror(r)); return -1; } printf("PIN changed.\n"); return 0; } static int do_unblock(int argc, char **argv) { int ref, r; 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; memset(&data, 0, sizeof(data)); data.cmd = SC_PIN_CMD_UNBLOCK; if (argc < 1 || argc > 3) return usage(do_unblock); if (strncasecmp(argv[0], "CHV", 3)) { fprintf(stderr, "Invalid type.\n"); return usage(do_unblock); } if (sscanf(argv[0] + 3, "%d", &ref) != 1) { printf("Invalid key reference.\n"); return usage(do_unblock); } if (argc > 1) { puklen = sizeof(puk); if (parse_string_or_hexdata(argv[1], puk, &puklen) != 0) { fprintf(stderr, "Invalid key value.\n"); return usage(do_unblock); } } if (argc > 2) { newpinlen = sizeof(newpin); if (parse_string_or_hexdata(argv[2], newpin, &newpinlen) != 0) { fprintf(stderr, "Invalid key value.\n"); return usage(do_unblock); } } data.pin_type = SC_AC_CHV; data.pin_reference = ref; data.pin1.data = puklen ? puk : NULL; data.pin1.len = puklen; data.pin2.data = newpinlen ? newpin : NULL; data.pin2.len = newpinlen; r = sc_lock(card); if (r == SC_SUCCESS) r = sc_pin_cmd(card, &data, NULL); sc_unlock(card); if (r) { if (r == SC_ERROR_PIN_CODE_INCORRECT) fprintf(stderr, "Incorrect code.\n"); fprintf(stderr, "Unable to unblock PIN code: %s\n", sc_strerror(r)); return -1; } printf("PIN unblocked.\n"); return 0; } static int do_get(int argc, char **argv) { u8 buf[SC_MAX_EXT_APDU_RESP_SIZE]; int r, err = 1; size_t count = 0; unsigned int idx = 0; sc_path_t path; sc_file_t *file = NULL; char *filename; FILE *outf = NULL; if (argc < 1 || argc > 2) return usage(do_get); if (arg_to_path(argv[0], &path, 0) != 0) return usage(do_get); filename = (argc == 2) ? argv[1] : path_to_filename(&path, '_', 0); 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; } 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 */ 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); goto err; } 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)) break; fwrite(buf, r, 1, outf); idx += r; count -= r; } if (outf == stdout) { fwrite("\n", 1, 1, outf); } else { printf("Total of %d bytes read from %s and saved to %s.\n", idx, 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(); 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(); return -err; } static int do_update_binary(int argc, char **argv) { u8 buf[SC_MAX_EXT_APDU_DATA_SIZE]; size_t buflen = sizeof(buf); int r, err = 1; unsigned long offs; sc_path_t path; sc_file_t *file; if (argc != 3) 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); r = parse_string_or_hexdata(argv[2], buf, &buflen); if (r < 0) { fprintf(stderr, "Unable to parse input data: %s\n", sc_strerror(r)); return -1; } 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); return -1; } if (file->ef_structure != SC_FILE_EF_TRANSPARENT) { fprintf(stderr, "EF structure should be SC_FILE_EF_TRANSPARENT\n"); goto err; } r = sc_lock(card); if (r == SC_SUCCESS) r = sc_update_binary(card, (unsigned)offs, buf, buflen, 0); sc_unlock(card); if (r < 0) { fprintf(stderr, "Cannot update %04X: %s\n", file->id, sc_strerror(r)); goto err; } printf("Total of %d bytes written to %04X at offset %lu.\n", r, file->id, offs); err = 0; err: sc_file_free(file); select_current_path_or_die(); return -err; } static int do_update_record(int argc, char **argv) { u8 buf[SC_MAX_EXT_APDU_DATA_SIZE]; size_t buflen; int r, err = 1; size_t rec, offs; sc_path_t path; sc_file_t *file; if (argc != 4) 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); 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); return -1; } if (file->ef_structure != SC_FILE_EF_LINEAR_VARIABLE) { fprintf(stderr, "EF structure should be SC_FILE_EF_LINEAR_VARIABLE\n"); goto err; } 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; } /* 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; 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_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 %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: sc_file_free(file); select_current_path_or_die(); return -err; } static int do_put(int argc, char **argv) { 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 *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, '_', 0); inf = fopen(filename, "rb"); if (inf == NULL) { perror(filename); goto err; } 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) { size_t c = count > sizeof(buf) ? sizeof(buf) : count; sz = fread(buf, 1, c, inf); if (sz < 0) { perror("fread"); goto err; } 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); goto err; } 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 to %04X.\n", idx, file->id); err = 0; err: sc_file_free(file); if (inf) fclose(inf); select_current_path_or_die(); return -err; } static int do_debug(int argc, char **argv) { int i; if (!argc) printf("Current debug level is %d\n", ctx->debug); else { if (sscanf(argv[0], "%d", &i) != 1) return -1; printf("Debug level set to %d\n", i); ctx->debug = i; if (i > 1) { sc_ctx_log_to_file(ctx, "stderr"); } } return 0; } static int do_erase(int argc, char **argv) { int r; if (argc != 0) return usage(do_erase); r = sc_lock(card); if (r == SC_SUCCESS) r = sc_card_ctl(card, SC_CARDCTL_ERASE_CARD, NULL); sc_unlock(card); if (r) { fprintf(stderr, "Failed to erase card: %s\n", sc_strerror(r)); return -1; } return 0; } static int do_random(int argc, char **argv) { unsigned char buffer[SC_MAX_EXT_APDU_BUFFER_SIZE] = {0}; int r, count; const char *filename = NULL; FILE *outf = NULL; if (argc < 1 || argc > 2) return usage(do_random); count = atoi(argv[0]); 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; } 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) { fprintf(stderr, "Failed to get random bytes: %s\n", sc_strerror(r)); if (argc == 2) { fclose(outf); } return -1; } 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) { 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]; r = sc_lock(card); if (r == SC_SUCCESS) r = sc_get_data(card, tag, buffer, sizeof(buffer)); sc_unlock(card); if (r < 0) { fprintf(stderr, "Failed to get DO %04X: %s\n", tag, sc_strerror(r)); return -1; } if (argc == 2) { const char *filename = argv[1]; 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); #ifdef _WIN32 if (fp == stdout) _setmode(fileno(stdout), _O_TEXT); #endif if (fp != stdout) fclose(fp); } else { printf("Data Object %04X:\n", tag & 0xFFFF); util_hex_dump_asc(stdout, buffer, r, 0); } return 0; } /** * Use PUT DATA command to write to Data Object. **/ static int do_put_data(int argc, char **argv) { u8 id[2] = { 0x00, 0x00 }; unsigned int tag; u8 buf[SC_MAX_EXT_APDU_DATA_SIZE]; size_t buflen = sizeof(buf); int r; if (argc != 2) return usage(do_put_data); 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) { fprintf(stderr, "Error parsing %s: %s\n", argv[1], sc_strerror(r)); return r; } /* Call OpenSC to do put data */ r = sc_lock(card); if (r == SC_SUCCESS) r = sc_put_data(card, tag, buf, buflen); sc_unlock(card); if (r < 0) { fprintf(stderr, "Failed to put data to DO %04X: %s\n", tag, sc_strerror(r)); return -1; } printf("Total of %d bytes written.\n", r); return 0; } static int do_apdu(int argc, char **argv) { sc_apdu_t apdu; 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); /* 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)); return r; }; len += len0; } r = sc_bytes2apdu(card->ctx, buf, len, &apdu); if (r) { fprintf(stderr, "Invalid APDU: %s\n", sc_strerror(r)); return 2; } apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); printf("Sending: "); util_hex_dump(stdout, buf, len, " "); printf("\n"); 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; } printf("Received (SW1=0x%02X, SW2=0x%02X)%s\n", apdu.sw1, apdu.sw2, apdu.resplen ? ":" : ""); if (apdu.resplen) util_hex_dump_asc(stdout, apdu.resp, apdu.resplen, -1); r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (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; u8 buf[SC_MAX_EXT_APDU_DATA_SIZE] = {0}; if (argc > 3) return usage(do_asn1); /* select file */ if (argc) { if (arg_to_path(argv[0], &path, 0) != 0) { fprintf(stderr, "Invalid file path\n"); return -1; } 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); goto err; } } else { path = current_path; file = current_file; not_current = 0; } if (file->type != SC_FILE_TYPE_WORKING_EF) { fprintf(stderr, "Only working EFs may be read\n"); goto err; } /* read */ 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; } } 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; } } /* 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: if (not_current) { sc_file_free(file); select_current_path_or_die(); } return -err; } static int do_sm(int argc, char **argv) { int r = SC_ERROR_NOT_SUPPORTED, ret = -1; if (argc != 1) return usage(do_sm); #ifdef ENABLE_SM if (!strcmp(argv[0],"open")) { if (!card->sm_ctx.ops.open) { 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) { fprintf(stderr, "Not supported\n"); return -1; } r = card->sm_ctx.ops.close(card); } #endif if (r == SC_SUCCESS) { ret = 0; printf("Success!\n"); } else { fprintf(stderr, "Failure: %s\n", sc_strerror(r)); } return ret; } static int do_help(int argc, char **argv) { struct command *cmd; printf("%s commands:\n", (argc) ? "Matching" : "Supported"); for (cmd = cmds; cmd->name; cmd++) { 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; } static int do_quit(int argc, char **argv) { die(0); return 0; } static int parse_cmdline(char *in, char **argv, int argvsize) { int argc; for (argc = 0; argc < argvsize-1; argc++) { in += strspn(in, " \t\n"); if (*in == '\0') { /* end of input reached */ argv[argc] = NULL; return argc; } if (*in == '"') { /* double-quoted string */ argv[argc] = in++; in += strcspn(in, "\""); if (*in++ != '"') { /* error: unbalanced quote */ argv[0] = NULL; return 0; } } else { /* white-space delimited word */ argv[argc] = in; in += strcspn(in, " \t\n"); } if (*in != '\0') *in++ = '\0'; } /* error: too many arguments - argv[] exhausted */ argv[0] = NULL; return 0; } static char *read_cmdline(FILE *script, char *prompt) { static char buf[SC_MAX_EXT_APDU_BUFFER_SIZE]; if (interactive) { #ifdef ENABLE_READLINE static int initialized; if (!initialized) { initialized = 1; using_history(); } char *line = readline(prompt); /* 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; #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) { fputc('\n', stdout); return NULL; } if (strlen(buf) == 0) return NULL; if (buf[strlen(buf)-1] == '\n') buf[strlen(buf)-1] = '\0'; return buf; } int main(int argc, char *argv[]) { int r, c, long_optind = 0, err = 0; sc_context_param_t ctx_param; int lcycle = SC_CARDCTRL_LIFECYCLE_ADMIN; FILE *script; printf("OpenSC Explorer version %s\n", sc_get_version()); while (1) { c = getopt_long(argc, argv, "r:c:vwm:", options, &long_optind); if (c == -1) break; if (c == '?') util_print_usage_and_die(app_name, options, option_help, "[SCRIPT]"); switch (c) { case 'r': opt_reader = optarg; break; case 'c': opt_driver = optarg; break; case 'w': opt_wait = 1; break; case 'v': verbose++; break; case 'm': opt_startfile = optarg; break; } } 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", sc_strerror(r)); return 1; } ctx->flags |= SC_CTX_FLAG_ENABLE_DEFAULT_DRIVER; if (verbose > 1) { ctx->debug = verbose; ctx->debug_file = stderr; } 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); err = 1; goto end; } } err = util_connect_card_ex(ctx, &card, opt_reader, opt_wait, 0); if (err) goto end; if (opt_startfile) { if(*opt_startfile) { char startpath[SC_MAX_PATH_STRING_SIZE * 2]; char *args[] = { startpath }; 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) { fprintf(stderr, "unable to select file %s: %s\n", opt_startfile, sc_strerror(r)); die(1); } } } else { sc_format_path("3F00", ¤t_path); r = sc_lock(card); if (r == SC_SUCCESS) r = sc_select_file(card, ¤t_path, ¤t_file); sc_unlock(card); if (r) { fprintf(stderr, "unable to select MF: %s\n", sc_strerror(r)); die(1); } } 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)); switch (argc - optind) { default: 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; } else if ((script = fopen(argv[optind], "r")) == NULL) { util_print_usage_and_die(app_name, options, option_help, "[SCRIPT]"); } break; } 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, '/', 0)); line = read_cmdline(script, prompt); if (line == NULL) break; cargc = parse_cmdline(line, cargv, DIM(cargv)); if ((cargc < 1) || (*cargv[0] == '#')) continue; cmd = ambiguous_match(cmds, cargv[0], &multiple); if (cmd == NULL) { fprintf(stderr, "%s command: %s\n", (multiple) ? "Ambiguous" : "Unknown", cargv[0]); if (interactive) do_help((multiple) ? 1 : 0, cargv); err = -1; } else { err = cmd->func(cargc-1, cargv+1); } } end: die(err); return 0; /* not reached */ } OpenSC-0.26.1/src/tools/opensc-notify-cmdline.c000066400000000000000000000447531474147347300212630ustar00rootroot00000000000000/* 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 : */ OpenSC-0.26.1/src/tools/opensc-notify-cmdline.h000066400000000000000000000200771474147347300212610ustar00rootroot00000000000000/** @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 */ OpenSC-0.26.1/src/tools/opensc-notify.c000066400000000000000000000162701474147347300176430ustar00rootroot00000000000000/* * 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 OpenSC-0.26.1/src/tools/opensc-notify.ggo.in000066400000000000000000000022221474147347300205720ustar00rootroot00000000000000package "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 " OpenSC-0.26.1/src/tools/opensc-tool.c000066400000000000000000000536301474147347300173110ustar00rootroot00000000000000/* * opensc-tool.c: Tool for accessing smart cards with libopensc * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include #include #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include #include "libopensc/opensc.h" #include "libopensc/cardctl.h" #include "util.h" /* type for associations of IDs to names */ typedef struct _id2str { unsigned int id; const char *str; } id2str_t; static const char *app_name = "opensc-tool"; static int opt_wait = 0; static char ** opt_apdus; static char *opt_reader; static int opt_apdu_count = 0; static int verbose = 0; enum { OPT_SERIAL = 0x100, OPT_LIST_ALG, OPT_VERSION, OPT_RESET }; // clang-format off static const struct option options[] = { { "version", 0, NULL, OPT_VERSION }, { "info", 0, NULL, 'i' }, { "atr", 0, NULL, 'a' }, { "serial", 0, NULL, OPT_SERIAL }, { "name", 0, NULL, 'n' }, { "get-conf-entry", 1, NULL, 'G' }, { "set-conf-entry", 1, NULL, 'S' }, { "list-readers", 0, NULL, 'l' }, { "list-drivers", 0, NULL, 'D' }, { "list-files", 0, NULL, 'f' }, { "send-apdu", 1, NULL, 's' }, { "reader", 1, NULL, 'r' }, { "reset", 2, NULL, OPT_RESET }, { "card-driver", 1, NULL, 'c' }, { "list-algorithms", 0, NULL, OPT_LIST_ALG }, { "wait", 0, NULL, 'w' }, { "verbose", 0, NULL, 'v' }, { NULL, 0, NULL, 0 } }; // clang-format on static const char *option_help[] = { "Prints OpenSC package revision", "Prints information about OpenSC", "Prints the ATR bytes of the card", "Prints the card serial number", "Identify the card and print its name", "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 (may need '-c default')", "Uses reader number [0]", "Does card reset of type [cold]", "Forces a card driver (use '?' for list)", "Lists algorithms supported by card", "Wait for a card to be inserted", "Verbose operation, may be used several times", }; static sc_context_t *ctx = NULL; static sc_card_t *card = NULL; static int opensc_info(void) { printf ( "%s %s ", PACKAGE_NAME, PACKAGE_VERSION ); #if defined(__VERSION__) printf ( "[%s %s]\n", #if defined(__GNUC__) "gcc ", #else "unknown ", #endif __VERSION__ ); #elif defined(__SUNPRO_C) printf ( "[Sun C %x.%x]\n", #if __SUNPRO_C > 0x590 (__SUNPRO_C >> 12), (__SUNPRO_C >> 4) & 0xFF #else (__SUNPRO_C >> 8), (__SUNPRO_C >> 4) & 0xF #endif ); #elif defined(_MSC_VER) printf ("[Microsoft %d]\n", _MSC_VER); #else printf ("[Unknown compiler, please report]"); #endif printf ("Enabled features:%s\n", OPENSC_FEATURES); return 0; } static int opensc_get_conf_entry(const char *config) { scconf_block *conf_block = NULL, **blocks; char *buffer = NULL; char *section = NULL; char *name = NULL; char *key = NULL; int r = 0; if (ctx->conf == NULL) { r = ENOENT; goto cleanup; } if ((buffer = strdup(config)) == NULL) { r = ENOMEM; goto cleanup; } section = buffer; name = strchr(section+1, ':'); key = name == NULL ? NULL : strchr(name+1, ':'); if (key == NULL) { r = EINVAL; goto cleanup; } *name = '\0'; name++; *key = '\0'; key++; blocks = scconf_find_blocks(ctx->conf, NULL, section, name); if (blocks && blocks[0]) conf_block = blocks[0]; free(blocks); if (conf_block != NULL) { const char *value = scconf_get_str(conf_block, key, NULL); if (value != NULL) { printf ("%s\n", value); } } r = 0; cleanup: if (buffer != NULL) free(buffer); return r; } static int opensc_set_conf_entry(const char *config) { scconf_block *conf_block = NULL, **blocks; char *buffer = NULL; char *section = NULL; char *name = NULL; char *key = NULL; char *value = NULL; int r = 0; if (ctx->conf == NULL) { r = ENOENT; goto cleanup; } if ((buffer = strdup(config)) == NULL) { r = ENOMEM; goto cleanup; } section = buffer; name = strchr(section+1, ':'); key = name == NULL ? NULL : strchr(name+1, ':'); value = key == NULL ? NULL : strchr(key+1, ':'); if (value == NULL) { r = EINVAL; goto cleanup; } *name = '\0'; name++; *key = '\0'; key++; *value = '\0'; value++; blocks = scconf_find_blocks(ctx->conf, NULL, section, name); if (blocks && blocks[0]) conf_block = blocks[0]; free(blocks); if (conf_block != NULL) { scconf_item *item; for (item = conf_block->items; item != NULL; item = item->next) { scconf_list *list; if ((item->type != SCCONF_ITEM_TYPE_VALUE) || (strcmp(item->key, key) != 0)) continue; list = item->value.list; scconf_list_destroy(list); list = NULL; scconf_list_add(&list, value); item->value.list = list; break; } if (item == NULL) scconf_put_str(conf_block, key, value); } /* Write */ if ((r = scconf_write(ctx->conf, ctx->conf->filename)) != 0) { fprintf(stderr, "scconf_write(): %s\n", strerror(r)); goto cleanup; } r = 0; cleanup: if (buffer != NULL) free(buffer); return r; } static int list_readers(void) { unsigned int i, rcount = sc_ctx_get_reader_count(ctx); if (rcount == 0) { printf("No smart card readers found.\n"); return 0; } printf("# Detected readers (%s)\n", ctx->reader_driver->short_name); printf("Nr. Card Features Name\n"); for (i = 0; i < rcount; i++) { sc_reader_t *reader = sc_ctx_get_reader(ctx, i); int state = sc_detect_card_presence(reader); printf("%-5d%-6s%-10s%s\n", i, state & SC_READER_CARD_PRESENT ? "Yes":"No", reader->capabilities & SC_READER_CAP_PIN_PAD ? "PIN pad":"", reader->name); if (state & SC_READER_CARD_PRESENT && verbose) { 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, ':'); if (state & SC_READER_CARD_EXCLUSIVE) printf(" %s [EXCLUSIVE]\n", tmp); else { if ((r = sc_connect_card(reader, &c)) != SC_SUCCESS) { fprintf(stderr, " failed: %s\n", sc_strerror(r)); } else { printf(" %s %s %s\n", tmp, c->name ? c->name : "", state & SC_READER_CARD_INUSE ? "[IN USE]" : ""); sc_disconnect_card(c); } } } } return 0; } static int print_file(sc_card_t *in_card, const sc_file_t *file, const sc_path_t *path, int depth) { int r; const char *tmps; for (r = 0; r < depth; r++) printf(" "); printf("%s ", sc_print_path(path)); if (file->namelen) { printf("["); util_print_binary(stdout, file->name, file->namelen); printf("] "); } switch (file->type) { case SC_FILE_TYPE_WORKING_EF: tmps = "wEF"; break; case SC_FILE_TYPE_INTERNAL_EF: tmps = "iEF"; break; case SC_FILE_TYPE_DF: tmps = "DF"; break; default: tmps = "unknown"; break; } printf("type: %s, ", tmps); if (file->type != SC_FILE_TYPE_DF) { const id2str_t ef_type_name[] = { { SC_FILE_EF_TRANSPARENT, "transparent" }, { SC_FILE_EF_LINEAR_FIXED, "linear-fixed" }, { SC_FILE_EF_LINEAR_FIXED_TLV, "linear-fixed (TLV)" }, { SC_FILE_EF_LINEAR_VARIABLE, "linear-variable" }, { SC_FILE_EF_LINEAR_VARIABLE_TLV, "linear-variable (TLV)" }, { SC_FILE_EF_CYCLIC, "cyclic" }, { SC_FILE_EF_CYCLIC_TLV, "cyclic (TLV)" }, { 0, NULL } }; const char *ef_type = "unknown"; for (r = 0; ef_type_name[r].str != NULL; r++) if (file->ef_structure == ef_type_name[r].id) ef_type = ef_type_name[r].str; printf("ef structure: %s, ", ef_type); } printf("size: %lu\n", (unsigned long) file->size); for (r = 0; r < depth; r++) printf(" "); if (file->type == SC_FILE_TYPE_DF) { const id2str_t ac_ops_df[] = { { SC_AC_OP_SELECT, "select" }, { SC_AC_OP_LOCK, "lock" }, { SC_AC_OP_DELETE, "delete" }, { SC_AC_OP_CREATE, "create" }, { SC_AC_OP_REHABILITATE, "rehab" }, { SC_AC_OP_INVALIDATE, "inval" }, { SC_AC_OP_LIST_FILES, "list" }, { 0, NULL } }; for (r = 0; ac_ops_df[r].str != NULL; r++) printf("%s[%s] ", ac_ops_df[r].str, util_acl_to_str(sc_file_get_acl_entry(file, ac_ops_df[r].id))); } else { const id2str_t ac_ops_ef[] = { { SC_AC_OP_READ, "read" }, { SC_AC_OP_UPDATE, "update" }, { SC_AC_OP_ERASE, "erase" }, { SC_AC_OP_WRITE, "write" }, { SC_AC_OP_REHABILITATE, "rehab" }, { SC_AC_OP_INVALIDATE, "inval" }, { 0, NULL } }; for (r = 0; ac_ops_ef[r].str != NULL; r++) printf("%s[%s] ", ac_ops_ef[r].str, util_acl_to_str(sc_file_get_acl_entry(file, ac_ops_ef[r].id))); } if (file->sec_attr_len) { printf("sec: "); /* Octets are as follows: * DF: select, lock, delete, create, rehab, inval * EF: read, update, write, erase, rehab, inval * 4 MSB's of the octet mean: * 0 = ALW, 1 = PIN1, 2 = PIN2, 4 = SYS, * 15 = NEV */ util_hex_dump(stdout, file->sec_attr, file->sec_attr_len, ":"); } if (file->prop_attr_len) { printf("\n"); for (r = 0; r < depth; r++) printf(" "); printf("prop: "); util_hex_dump(stdout, file->prop_attr, file->prop_attr_len, ":"); } printf("\n\n"); if (file->type == SC_FILE_TYPE_DF) return 0; if (file->ef_structure == SC_FILE_EF_TRANSPARENT) { unsigned char *buf; if (!(buf = malloc(file->size))) { fprintf(stderr, "out of memory"); return 1; } 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]; 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); } } return 0; } static int enum_dir(sc_path_t path, int depth) { sc_file_t *file; int r, file_type; u8 files[SC_MAX_EXT_APDU_BUFFER_SIZE]; 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; } print_file(card, file, &path, depth); file_type = file->type; sc_file_free(file); if (file_type == SC_FILE_TYPE_DF) { int i; 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); } } } return 0; } static int list_files(void) { sc_path_t path; int r; sc_format_path("3F00", &path); r = enum_dir(path, 0); return r; } static int send_apdu(void) { sc_apdu_t apdu; u8 buf[SC_MAX_EXT_APDU_BUFFER_SIZE], rbuf[SC_MAX_EXT_APDU_BUFFER_SIZE]; size_t len0, r; int c, rc; for (c = 0; c < opt_apdu_count; c++) { len0 = sizeof(buf); sc_hex_to_bin(opt_apdus[c], buf, &len0); rc = sc_bytes2apdu(card->ctx, buf, len0, &apdu); if (rc) { fprintf(stderr, "Invalid APDU: %s\n", sc_strerror(rc)); return 2; } apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); printf("Sending: "); for (r = 0; r < len0; r++) printf("%02X ", buf[r]); printf("\n"); 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, apdu.resplen ? ":" : ""); if (apdu.resplen) util_hex_dump_asc(stdout, apdu.resp, apdu.resplen, -1); } return 0; } static void print_serial(sc_card_t *in_card) { int r; sc_serial_number_t 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 util_hex_dump_asc(stdout, serial.value, serial.len, -1); } 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_EC, "ec" }, { SC_ALGORITHM_EDDSA, "eddsa" }, { SC_ALGORITHM_GOSTR3410, "gostr3410" }, { SC_ALGORITHM_DES, "des" }, { SC_ALGORITHM_3DES, "3des" }, { SC_ALGORITHM_GOST, "gost" }, { SC_ALGORITHM_MD5, "md5" }, { SC_ALGORITHM_SHA1, "sha1" }, { SC_ALGORITHM_GOSTR3411, "gostr3411" }, { SC_ALGORITHM_PBKDF2, "pbkdf2" }, { SC_ALGORITHM_PBES2, "pbes2" }, { SC_ALGORITHM_AES, "aes" }, { 0, NULL } }; const id2str_t alg_flag_names[] = { { SC_ALGORITHM_ONBOARD_KEY_GEN, "onboard key generation" }, { SC_ALGORITHM_NEED_USAGE, "needs usage" }, { 0, NULL } }; const id2str_t rsa_flag_names[] = { { 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); for (i=0; i < card->algorithm_count; i++) { int j; /* find algorithm name */ for (j = 0; alg_type_names[j].str != NULL; j++) { if (card->algorithms[i].algorithm == alg_type_names[j].id) { aname = alg_type_names[j].str; break; } } printf("Algorithm: %s\n", aname); printf("Key length: %zu\n", card->algorithms[i].key_length); printf("Flags:"); /* print general flags */ for (j = 0; alg_flag_names[j].str != NULL; j++) if (card->algorithms[i].flags & alg_flag_names[j].id) printf(" %s", alg_flag_names[j].str); /* print RSA specific flags */ if ( card->algorithms[i].algorithm == SC_ALGORITHM_RSA) { int padding = card->algorithms[i].flags & SC_ALGORITHM_RSA_PADS; int hashes = card->algorithms[i].flags & SC_ALGORITHM_RSA_HASHES; /* print RSA padding flags */ printf(" padding ("); for (j = 0; rsa_flag_names[j].str != NULL; j++) if (padding & rsa_flag_names[j].id) printf(" %s", rsa_flag_names[j].str); if (padding == SC_ALGORITHM_RSA_PAD_NONE) printf(" none"); printf(" ) "); /* print RSA hash flags */ printf("hashes ("); for (j = 0; rsa_flag_names[j].str != NULL; j++) if (hashes & rsa_flag_names[j].id) printf(" %s", rsa_flag_names[j].str); if (hashes == SC_ALGORITHM_RSA_HASH_NONE) printf(" none"); printf(" )"); } printf("\n"); if (card->algorithms[i].algorithm == SC_ALGORITHM_RSA && card->algorithms[i].u._rsa.exponent) { printf("RSA public exponent: %lu\n", (unsigned long) card->algorithms[i].u._rsa.exponent); } if (i < card->algorithm_count) printf("\n"); } return 0; } static int card_reset(const char *reset_type) { int cold_reset; int r; if (reset_type && strcmp(reset_type, "cold") && strcmp(reset_type, "warm")) { fprintf(stderr, "Invalid reset type: %s\n", reset_type); return 2; } cold_reset = !reset_type || strcmp(reset_type, "cold") == 0; 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); return 1; } return 0; } int main(int argc, char *argv[]) { int err = 0, r, c, long_optind = 0; int do_info = 0; int do_get_conf_entry = 0; int do_set_conf_entry = 0; int do_list_readers = 0; int do_list_drivers = 0; int do_list_files = 0; int do_send_apdu = 0; int do_print_atr = 0; int do_print_version = 0; int do_print_serial = 0; int do_print_name = 0; int do_list_algorithms = 0; int do_reset = 0; int action_count = 0; const char *opt_driver = NULL; 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; while (1) { c = getopt_long(argc, argv, "inlG:S:fr:vs:Dc:aw", options, &long_optind); if (c == -1) break; if (c == '?') util_print_usage_and_die(app_name, options, option_help, NULL); switch (c) { case 'i': do_info = 1; action_count++; break; case 'G': do_get_conf_entry = 1; opt_conf_entry = optarg; action_count++; break; case 'S': do_set_conf_entry = 1; opt_conf_entry = optarg; action_count++; break; case 'l': do_list_readers = 1; action_count++; break; case 'D': do_list_drivers = 1; action_count++; break; case 'f': do_list_files = 1; action_count++; break; case 's': p = (char **) realloc(opt_apdus, (opt_apdu_count + 1) * sizeof(char *)); if (!p) { fprintf(stderr, "Not enough memory\n"); err = 1; goto end; } opt_apdus = p; opt_apdus[opt_apdu_count] = optarg; do_send_apdu++; if (opt_apdu_count == 0) action_count++; opt_apdu_count++; break; case 'a': do_print_atr = 1; action_count++; break; case 'n': do_print_name = 1; action_count++; break; case 'r': opt_reader = optarg; break; case 'v': verbose++; break; case OPT_VERSION: do_print_version = 1; action_count++; 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; break; case OPT_SERIAL: do_print_serial = 1; action_count++; break; case OPT_LIST_ALG: do_list_algorithms = 1; action_count++; break; case OPT_RESET: do_reset = 1; opt_reset_type = optarg; action_count++; break; } } if (action_count == 0) util_print_usage_and_die(app_name, options, option_help, NULL); if (do_print_version) { printf("%s\n", OPENSC_SCM_REVISION); action_count--; } if (do_info) { opensc_info(); action_count--; } 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", sc_strerror(r)); return 1; } ctx->flags |= SC_CTX_FLAG_ENABLE_DEFAULT_DRIVER; if (do_get_conf_entry) { if ((err = opensc_get_conf_entry (opt_conf_entry))) goto end; action_count--; } if (do_set_conf_entry) { if ((err = opensc_set_conf_entry (opt_conf_entry))) goto end; action_count--; } if (do_list_readers) { if ((err = list_readers())) goto end; action_count--; } if (do_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) { fprintf(stderr, "Driver '%s' not found!\n", opt_driver); err = 1; goto end; } } if (verbose) printf("Connecting to card in reader %s...\n", reader->name); 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:"); print_serial(card); action_count--; } if (do_print_name) { if (verbose) printf("Card name: "); printf("%s\n", card->name); action_count--; } if (do_send_apdu) { if ((err = send_apdu())) goto end; action_count--; } if (do_list_files) { if ((err = list_files())) goto end; action_count--; } if (do_list_algorithms) { if ((err = list_algorithms())) goto end; action_count--; } if (do_reset) { if ((err = card_reset(opt_reset_type))) goto end; action_count--; } end: sc_disconnect_card(card); sc_release_context(ctx); return err; } OpenSC-0.26.1/src/tools/org.opensc-project.mac.opensc-notify.plist.in000066400000000000000000000010411474147347300254260ustar00rootroot00000000000000 Label org.opensc-project.mac.opensc-notify Program @bindir@/opensc-notify RunAtLoad StandardErrorPath /dev/null StandardOutPath /dev/null OpenSC-0.26.1/src/tools/org.opensc-project.mac.pkcs11-register.plist.in000066400000000000000000000010451474147347300255610ustar00rootroot00000000000000 Label org.opensc-project.mac.pkcs11-register Program @bindir@/pkcs11-register RunAtLoad StandardErrorPath /dev/null StandardOutPath /dev/null OpenSC-0.26.1/src/tools/org.opensc.notify.desktop.in000066400000000000000000000003661474147347300222650ustar00rootroot00000000000000[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 OpenSC-0.26.1/src/tools/piv-tool.c000066400000000000000000000472361474147347300166250ustar00rootroot00000000000000/* * piv-tool.c: Tool for accessing smart cards with libopensc * * Copyright (C) 2001 Juha Yrjölä * Copyright (C) 2005,2010 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 * 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 #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include /* Module only built if OPENSSL is enabled */ #include #include #include #include #include #if OPENSSL_VERSION_NUMBER >= 0x30000000L # include # include #endif #if !defined(OPENSSL_NO_EC) && !defined(OPENSSL_NO_ECDSA) #include #include #endif #include #include #include #include #include #include #include #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"; static int opt_wait = 0; static char ** opt_apdus; static char * opt_reader; static int opt_apdu_count = 0; static int verbose = 0; enum { OPT_SERIAL = 0x100, }; static const struct option options[] = { { "serial", 0, NULL, OPT_SERIAL }, { "name", 0, NULL, 'n' }, { "admin", 1, NULL, 'A' }, { "genkey", 1, NULL, 'G' }, { "object", 1, NULL, 'O' }, { "cert", 1, NULL, 'C' }, { "compresscert", 1, NULL, 'Z' }, { "out", 1, NULL, 'o' }, { "in", 1, NULL, 'i' }, { "send-apdu", 1, NULL, 's' }, { "reader", 1, NULL, 'r' }, { "wait", 0, NULL, 'w' }, { "verbose", 0, NULL, 'v' }, { NULL, 0, NULL, 0 } }; static const char *option_help[] = { "Print the card serial number", "Identify the card and print its name", "Authenticate using default 3DES key", "Generate key : 9A:06 on card, and output pubkey", "Load an object containerID as defined in 800-73 without leading 0x", "Load a cert where is 9A,9C,9D or 9E", "Load a cert that has been gzipped ", "Output file for cert or key", "Input file for cert", "Sends an APDU in format AA:BB:CC:DD:EE:FF...", "Uses reader number [0]", "Wait for a card to be inserted", "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 int load_object(const char * object_id, const char * object_file) { FILE *fp = NULL; sc_path_t path; size_t derlen; u8 *der = NULL; u8 *body; size_t bodylen; int r = -1; struct stat stat_buf; 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); goto err; } derlen = stat_buf.st_size; der = malloc(derlen); if (der == NULL) { printf("file %s is too big, %lu\n", object_file, (unsigned long)derlen); goto err; } if (1 != fread(der, derlen, 1, fp)) { printf("unable to read file %s\n",object_file); goto err; } /* 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) { fprintf(stderr, "object tag or length not valid\n"); goto err; } sc_format_path(object_id, &path); r = sc_select_file(card, &path, NULL); if (r < 0) { fprintf(stderr, "select file failed\n"); r = -1; goto err; } /* leave 8 bits for flags, and pass in total length */ r = sc_write_binary(card, 0, der, derlen, derlen<<8); err: free(der); if (fp) fclose(fp); return r; } static int load_cert(const char * cert_id, const char * cert_file, int compress) { X509 * cert = NULL; FILE *fp = NULL; u8 buf[1]; size_t buflen = 1; sc_path_t path; u8 *der = NULL; u8 *p; size_t derlen; int r = -1; if (!cert_file) { printf("Missing cert file\n"); goto err; } 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 gzipped already */ struct stat stat_buf; if (0 != stat(cert_file, &stat_buf)) { printf("unable to read file %s\n", cert_file); goto err; } derlen = stat_buf.st_size; der = malloc(derlen); if (der == NULL) { printf("file %s is too big, %lu\n", cert_file, (unsigned long)derlen); goto err; } if (1 != fread(der, derlen, 1, fp)) { printf("unable to read file %s\n", cert_file); goto err; } } else { cert = PEM_read_X509(fp, &cert, NULL, NULL); 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); if (!der) { goto err; } p = der; i2d_X509(cert, &p); } sc_hex_to_bin(cert_id, buf,&buflen); switch (buf[0]) { case 0x9a: sc_format_path("0101",&path); break; case 0x9c: sc_format_path("0100",&path); break; case 0x9d: sc_format_path("0102",&path); break; case 0x9e: sc_format_path("0500",&path); break; default: fprintf(stderr,"cert must be 9A, 9C, 9D or 9E\n"); r = 2; goto err; } r = sc_select_file(card, &path, NULL); if (r < 0) { fprintf(stderr, "select file failed\n"); goto err; } /* 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); err: free(der); if (fp) fclose(fp); return r; } static int admin_mode(const char* admin_info) { int r; u8 opts[3]; size_t buflen = 2; 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; } r = sc_card_ctl(card, SC_CARDCTL_PIV_AUTHENTICATE, &opts); if (r) fprintf(stderr, " admin_mode failed %d\n", r); return r; } /* generate a new key pair, and save public key in newkey */ static int gen_key(const char * key_info) { int r; u8 buf[2]; size_t buflen = 2; sc_cardctl_piv_genkey_info_t keydata = {0, 0, 0, NULL, 0, NULL, 0, NULL, 0, NULL, 0}; #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"); return 2; } switch (buf[0]) { case 0x9a: case 0x9c: case 0x9d: case 0x9e: keydata.key_num = buf[0]; break; default: fprintf(stderr, ": must be 9A, 9C, 9D or 9E\n"); return 2; } switch (buf[1]) { case 0x05: keydata.key_bits = 3072; break; case 0x06: keydata.key_bits = 1024; break; case 0x07: keydata.key_bits = 2048; break; #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; #endif default: fprintf(stderr, ": algid=RSA - 05, 06, 07 for 3072, 1024, 2048;EC - 11, 14 for 256, 384\n"); return 2; } keydata.key_algid = buf[1]; r = sc_card_ctl(card, SC_CARDCTL_PIV_GENERATE_KEY, &keydata); if (r) { fprintf(stderr, "gen_key failed %d\n", r); return r; } #if OPENSSL_VERSION_NUMBER < 0x30000000L evpkey = EVP_PKEY_new(); if (!evpkey) { sc_log_openssl(ctx); fprintf(stderr, "allocation of key failed\n"); return r; } #endif if (keydata.key_bits > 0) { /* RSA key */ BIGNUM *newkey_n, *newkey_e; #if OPENSSL_VERSION_NUMBER < 0x30000000L RSA *newkey = RSA_new(); if (!newkey) { sc_log_openssl(ctx); EVP_PKEY_free(evpkey); free(keydata.pubkey); free(keydata.exponent); fprintf(stderr, "gen_key RSA_new failed %d\n",r); return -1; } #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 %d\n", r); free(keydata.pubkey); free(keydata.exponent); EVP_PKEY_free(evpkey); #if OPENSSL_VERSION_NUMBER < 0x30000000L RSA_free(newkey); #endif return -1; } newkey_n = BN_bin2bn(keydata.pubkey, (int)keydata.pubkey_len, NULL); newkey_e = BN_bin2bn(keydata.exponent, (int)keydata.exponent_len, NULL); free(keydata.pubkey); keydata.pubkey_len = 0; free(keydata.exponent); keydata.exponent_len = 0; if (!newkey_n || !newkey_e) { sc_log_openssl(ctx); EVP_PKEY_free(evpkey); #if OPENSSL_VERSION_NUMBER < 0x30000000L RSA_free(newkey); #endif fprintf(stderr, "conversion or key params failed %d\n", r); return -1; } #if OPENSSL_VERSION_NUMBER < 0x30000000L if (RSA_set0_key(newkey, newkey_n, newkey_e, NULL) != 1) { sc_log_openssl(ctx); EVP_PKEY_free(evpkey); RSA_free(newkey); BN_free(newkey_n); BN_free(newkey_e); fprintf(stderr, "gen_key unable to set RSA values"); return -1; } if (verbose) RSA_print_fp(stdout, newkey, 0); if (EVP_PKEY_assign_RSA(evpkey, newkey) != 1) { sc_log_openssl(ctx); EVP_PKEY_free(evpkey); RSA_free(newkey); BN_free(newkey_n); BN_free(newkey_e); fprintf(stderr, "gen_key unable to set RSA values"); return -1; } #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); return -1; } 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"); return -1; } if (verbose) EVP_PKEY_print_public_fp(stdout, evpkey, 0, NULL); EVP_PKEY_CTX_free(cctx); OSSL_PARAM_free(params); #endif } else { /* EC key */ #if !defined(OPENSSL_NO_EC) int i; 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 %d\n", r); EVP_PKEY_free(evpkey); return -1; } 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 = (int)(keydata.ecpoint_len - 1) / 2; x = BN_bin2bn(keydata.ecpoint + 1, i, NULL); y = BN_bin2bn(keydata.ecpoint + 1 + i, i, NULL) ; if (!x || !y) { sc_log_openssl(ctx); free(keydata.ecpoint); keydata.ecpoint_len = 0; BN_free(x); BN_free(y); EVP_PKEY_free(evpkey); EC_GROUP_free(ecgroup); EC_POINT_free(ecpoint); return -1; } r = EC_POINT_set_affine_coordinates(ecgroup, ecpoint, x, y, NULL); free(keydata.ecpoint); keydata.ecpoint_len = 0; BN_free(x); BN_free(y); if (r == 0) { sc_log_openssl(ctx); fprintf(stderr, "EC_POINT_set_affine_coordinates_GFp failed\n"); EVP_PKEY_free(evpkey); EC_GROUP_free(ecgroup); EC_POINT_free(ecpoint); return -1; } #if OPENSSL_VERSION_NUMBER < 0x30000000L eckey = EC_KEY_new(); 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"); EVP_PKEY_free(evpkey); EC_POINT_free(ecpoint); return -1; } 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"); EVP_PKEY_free(evpkey); return -1; } if (verbose) EC_KEY_print_fp(stdout, eckey, 0); if (EVP_PKEY_assign_EC_KEY(evpkey, eckey) != 1) { sc_log_openssl(ctx); EVP_PKEY_free(evpkey); return -1; } #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); return -1; } 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); return -1; } 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); return -1; } 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); return -1; } 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) r = i2d_PUBKEY_bio(bp, evpkey); if (evpkey) EVP_PKEY_free(evpkey); return r; } static int send_apdu(void) { sc_apdu_t apdu; u8 buf[SC_MAX_APDU_BUFFER_SIZE+3]; u8 rbuf[8192]; size_t len0, i; int r; int c; 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)); return 2; } apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); printf("Sending: "); for (i = 0; i < len0; i++) printf("%02X ", buf[i]); printf("\n"); r = sc_transmit_apdu(card, &apdu); if (r) { fprintf(stderr, "APDU transmit failed: %s\n", sc_strerror(r)); return 1; } printf("Received (SW1=0x%02X, SW2=0x%02X)%s\n", apdu.sw1, apdu.sw2, apdu.resplen ? ":" : ""); if (apdu.resplen) util_hex_dump_asc(stdout, apdu.resp, apdu.resplen, -1); } return 0; } 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); if (r < 0) fprintf(stderr, "sc_card_ctl(*, SC_CARDCTL_GET_SERIALNR, *) failed %d\n", r); else util_hex_dump_asc(stdout, serial.value, serial.len, -1); } int main(int argc, char *argv[]) { int err = 0, r, c; int do_send_apdu = 0; int do_admin_mode = 0; int do_gen_key = 0; int do_load_cert = 0; int do_load_object = 0; int compress_cert = 0; int do_print_serial = 0; int do_print_name = 0; int action_count = 0; const char *out_file = NULL; const char *in_file = NULL; const char *cert_id = NULL; const char *object_id = NULL; const char *key_info = NULL; const char *admin_info = NULL; sc_context_param_t ctx_param; char **old_apdus = 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; } opt_apdus[opt_apdu_count] = optarg; do_send_apdu++; if (opt_apdu_count == 0) action_count++; opt_apdu_count++; break; case 'n': do_print_name = 1; action_count++; break; case 'A': do_admin_mode = 1; admin_info = optarg; action_count++; break; case 'G': do_gen_key = 1; key_info = optarg; action_count++; break; case 'O': do_load_object = 1; object_id = optarg; action_count++; break; case 'Z': compress_cert = 1; /* fall through */ case 'C': do_load_cert = 1; cert_id = optarg; action_count++; break; case 'i': in_file = optarg; break; case 'o': out_file = optarg; break; case 'r': opt_reader = optarg; break; case 'v': verbose++; 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(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; } else { bp = BIO_new(BIO_s_file()); BIO_set_fp(bp,stdout,BIO_NOCLOSE); } 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) { fprintf(stderr, "Failed to establish context: %s\n", sc_strerror(r)); return 1; } if (action_count <= 0) 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); 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; action_count--; } if (do_send_apdu) { /* can use pin before load cert for a beta card */ if ((err = send_apdu())) goto end; action_count--; } if (do_gen_key) { if ((err = gen_key(key_info))) goto end; action_count--; } if (do_load_object) { if ((err = load_object(object_id, in_file))) goto end; action_count--; } if (do_load_cert) { if ((err = load_cert(cert_id, in_file, compress_cert))) goto end; action_count--; } if (do_print_serial) { if (verbose) printf("Card serial number:"); print_serial(card); action_count--; } if (do_print_name) { if (verbose) printf("Card name: "); printf("%s\n", card->name); action_count--; } end: if (bp) BIO_free(bp); if (card) { sc_unlock(card); sc_disconnect_card(card); } if (opt_apdus) free(opt_apdus); sc_release_context(ctx); ERR_print_errors_fp(stderr); return err; } OpenSC-0.26.1/src/tools/pkcs11-register-cmdline.c000066400000000000000000000402731474147347300214030ustar00rootroot00000000000000/* 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 Don't install module to Chrome (default=on)", " --skip-firefox Don't install module to Firefox (default=on)", " --skip-thunderbird Don't install module to Thunderbird (default=off)", " --skip-seamonkey Don't install module to SeaMonkey (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->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_flag = 1; args_info->skip_firefox_flag = 1; args_info->skip_thunderbird_flag = 0; args_info->skip_seamonkey_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->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)); 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->module_given) write_into_file(outfile, "module", args_info->module_orig, 0); if (args_info->skip_chrome_given) write_into_file(outfile, "skip-chrome", 0, 0 ); if (args_info->skip_firefox_given) write_into_file(outfile, "skip-firefox", 0, 0 ); if (args_info->skip_thunderbird_given) write_into_file(outfile, "skip-thunderbird", 0, 0 ); if (args_info->skip_seamonkey_given) write_into_file(outfile, "skip-seamonkey", 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 */ } 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", 0, NULL, 0 }, { "skip-firefox", 0, NULL, 0 }, { "skip-thunderbird", 0, NULL, 0 }, { "skip-seamonkey", 0, 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_flag), 0, &(args_info->skip_chrome_given), &(local_args_info.skip_chrome_given), optarg, 0, 0, ARG_FLAG, check_ambiguity, override, 1, 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_flag), 0, &(args_info->skip_firefox_given), &(local_args_info.skip_firefox_given), optarg, 0, 0, ARG_FLAG, check_ambiguity, override, 1, 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_flag), 0, &(args_info->skip_thunderbird_given), &(local_args_info.skip_thunderbird_given), optarg, 0, 0, ARG_FLAG, check_ambiguity, override, 1, 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_flag), 0, &(args_info->skip_seamonkey_given), &(local_args_info.skip_seamonkey_given), optarg, 0, 0, ARG_FLAG, check_ambiguity, override, 1, 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 : */ OpenSC-0.26.1/src/tools/pkcs11-register-cmdline.h000066400000000000000000000167631474147347300214170ustar00rootroot00000000000000/** @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 /** @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. */ int skip_chrome_flag; /**< @brief Don't install module to Chrome (default=on). */ const char *skip_chrome_help; /**< @brief Don't install module to Chrome help description. */ int skip_firefox_flag; /**< @brief Don't install module to Firefox (default=on). */ const char *skip_firefox_help; /**< @brief Don't install module to Firefox help description. */ int skip_thunderbird_flag; /**< @brief Don't install module to Thunderbird (default=off). */ const char *skip_thunderbird_help; /**< @brief Don't install module to Thunderbird help description. */ int skip_seamonkey_flag; /**< @brief Don't install module to SeaMonkey (default=off). */ 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); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* PKCS11_REGISTER_CMDLINE_H */ OpenSC-0.26.1/src/tools/pkcs11-register.c000066400000000000000000000226041474147347300177700ustar00rootroot00000000000000/* * 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 {"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); } #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_flag) add_module_chrome(module_path, module_name, exclude_module_path); if (!cmdline.skip_firefox_flag) add_module_firefox(module_path, module_name, exclude_module_path); if (!cmdline.skip_thunderbird_flag) add_module_thunderbird(module_path, module_name, exclude_module_path); if (!cmdline.skip_seamonkey_flag) add_module_seamonkey(module_path, module_name, exclude_module_path); cmdline_parser_free (&cmdline); return 0; } OpenSC-0.26.1/src/tools/pkcs11-register.desktop.in000066400000000000000000000003621474147347300216210ustar00rootroot00000000000000[Desktop Entry] Name=Install OpenSC PKCS#11 module Type=Application Exec=@bindir@/pkcs11-register --skip-chrome=off --skip-firefox=off Categories=Security;System; X-GNOME-AutoRestart=true X-GNOME-Autostart-Phase=Initialization NoDisplay=true OpenSC-0.26.1/src/tools/pkcs11-register.ggo.in000066400000000000000000000013001474147347300207150ustar00rootroot00000000000000package "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" flag @PKCS11_REGISTER_SKIP_FIREFOX@ option "skip-firefox" - "Don't install module to Firefox" flag @PKCS11_REGISTER_SKIP_FIREFOX@ option "skip-thunderbird" - "Don't install module to Thunderbird" flag off option "skip-seamonkey" - "Don't install module to SeaMonkey" flag off text " Report bugs to @PACKAGE_BUGREPORT@ Written by Frank Morgner " OpenSC-0.26.1/src/tools/pkcs11-tool.c000066400000000000000000010657731474147347300171400ustar00rootroot00000000000000/* * pkcs11-tool.c: Tool for poking around pkcs11 modules/tokens * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include #include #include #include #include #include #ifndef _WIN32 #include #include #include #ifdef HAVE_PTHREAD #include #endif #else #include #include #endif #ifdef ENABLE_OPENSSL #include #include #include #include #include #include #include #include #include #include #if OPENSSL_VERSION_NUMBER >= 0x30000000L # include # include #include #endif #if !defined(OPENSSL_NO_EC) && !defined(OPENSSL_NO_ECDSA) #include #include #endif #include #include #endif #include "pkcs11/pkcs11.h" #include "pkcs11/pkcs11-opensc.h" #include "libopensc/asn1.h" #include "libopensc/log.h" #include "common/compat_strlcat.h" #include "common/compat_strlcpy.h" #include "common/libpkcs11.h" #include "util.h" #include "libopensc/sc-ossl-compat.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 #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 static struct ec_curve_info { const char *name; const char *oid; const char *ec_params; size_t size; CK_KEY_TYPE mechanism; } ec_curve_infos[] = { {"secp192r1", "1.2.840.10045.3.1.1", "06082A8648CE3D030101", 192, 0}, {"prime192v1", "1.2.840.10045.3.1.1", "06082A8648CE3D030101", 192, 0}, {"prime192v2", "1.2.840.10045.3.1.2", "06082A8648CE3D030102", 192, 0}, {"prime192v3", "1.2.840.10045.3.1.3", "06082A8648CE3D030103", 192, 0}, {"nistp192", "1.2.840.10045.3.1.1", "06082A8648CE3D030101", 192, 0}, {"ansiX9p192r1", "1.2.840.10045.3.1.1", "06082A8648CE3D030101", 192, 0}, {"secp224r1", "1.3.132.0.33", "06052b81040021", 224, 0}, {"nistp224", "1.3.132.0.33", "06052b81040021", 224, 0}, {"prime256v1", "1.2.840.10045.3.1.7", "06082A8648CE3D030107", 256, 0}, {"secp256r1", "1.2.840.10045.3.1.7", "06082A8648CE3D030107", 256, 0}, {"ansiX9p256r1", "1.2.840.10045.3.1.7", "06082A8648CE3D030107", 256, 0}, {"frp256v1", "1.2.250.1.223.101.256.1", "060a2a817a01815f65820001", 256, 0}, {"secp384r1", "1.3.132.0.34", "06052B81040022", 384, 0}, {"prime384v1", "1.3.132.0.34", "06052B81040022", 384, 0}, {"ansiX9p384r1", "1.3.132.0.34", "06052B81040022", 384, 0}, {"prime521v1", "1.3.132.0.35", "06052B81040023", 521, 0}, {"secp521r1", "1.3.132.0.35", "06052B81040023", 521, 0}, {"nistp521", "1.3.132.0.35", "06052B81040023", 521, 0}, {"brainpoolP192r1", "1.3.36.3.3.2.8.1.1.3", "06092B2403030208010103", 192, 0}, {"brainpoolP224r1", "1.3.36.3.3.2.8.1.1.5", "06092B2403030208010105", 224, 0}, {"brainpoolP256r1", "1.3.36.3.3.2.8.1.1.7", "06092B2403030208010107", 256, 0}, {"brainpoolP320r1", "1.3.36.3.3.2.8.1.1.9", "06092B2403030208010109", 320, 0}, {"brainpoolP384r1", "1.3.36.3.3.2.8.1.1.11", "06092B240303020801010B", 384, 0}, {"brainpoolP512r1", "1.3.36.3.3.2.8.1.1.13", "06092B240303020801010D", 512, 0}, {"secp192k1", "1.3.132.0.31", "06052B8104001F", 192, 0}, {"secp256k1", "1.3.132.0.10", "06052B8104000A", 256, 0}, {"secp521k1", "1.3.132.0.35", "06052B81040023", 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", "130c656477617264733235353139", 255, CKM_EC_EDWARDS_KEY_PAIR_GEN}, /* send by curve name */ {"curve25519", "1.3.6.1.4.1.3029.1.5.1", "130a63757276653235353139", 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", "06032b656e", 255, CKM_EC_MONTGOMERY_KEY_PAIR_GEN}, /* RFC 4810 send by OID */ {"X448", "1.3.101.111", "06032b656f", 448, CKM_EC_MONTGOMERY_KEY_PAIR_GEN}, /* RFC 4810 send by OID */ {"Ed25519", "1.3.101.112", "06032b6570", 255, CKM_EC_EDWARDS_KEY_PAIR_GEN}, /* RFC 4810 send by OID */ {"Ed448", "1.3.101.113", "06032b6571", 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", "06032b656e", 255, CKM_EC_MONTGOMERY_KEY_PAIR_GEN}, {"ed25519", "1.3.101.112", "06032b6570", 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}, }; // 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, OPT_SLOT, OPT_SLOT_DESCRIPTION, OPT_SLOT_INDEX, OPT_TOKEN_LABEL, OPT_APPLICATION_LABEL, OPT_APPLICATION_ID, OPT_ISSUER, OPT_SUBJECT, OPT_SO_PIN, OPT_INIT_TOKEN, OPT_INIT_PIN, OPT_ATTR_FROM, OPT_KEY_TYPE, OPT_KEY_USAGE_SIGN, OPT_KEY_USAGE_DECRYPT, OPT_KEY_USAGE_DERIVE, OPT_KEY_USAGE_WRAP, 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 }; // clang-format off static const struct option options[] = { { "module", 1, NULL, OPT_MODULE }, { "show-info", 0, NULL, 'I' }, { "list-slots", 0, NULL, 'L' }, { "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' }, { "puk", 1, NULL, OPT_PUK }, { "new-pin", 1, NULL, OPT_NEW_PIN }, { "so-pin", 1, NULL, OPT_SO_PIN }, { "init-token", 0, NULL, OPT_INIT_TOKEN }, { "init-pin", 0, NULL, OPT_INIT_PIN }, { "change-pin", 0, NULL, 'c' }, { "unlock-pin", 0, NULL, OPT_UNLOCK_PIN }, { "keypairgen", 0, NULL, 'k' }, { "keygen", 0, NULL, OPT_GENERATE_KEY }, { "key-type", 1, NULL, OPT_KEY_TYPE }, { "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 }, { "write-object", 1, NULL, 'w' }, { "read-object", 0, NULL, 'r' }, { "delete-object", 0, NULL, 'b' }, { "application-label", 1, NULL, OPT_APPLICATION_LABEL }, { "application-id", 1, NULL, OPT_APPLICATION_ID }, { "issuer", 1, NULL, OPT_ISSUER }, { "subject", 1, NULL, OPT_SUBJECT }, { "type", 1, NULL, 'y' }, { "id", 1, NULL, 'd' }, { "label", 1, NULL, 'a' }, { "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 }, { "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}, { 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", "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, 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", "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", #endif "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)", }; 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_signature_file = NULL; static const char * opt_module = DEFAULT_PKCS11_PROVIDER; 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; static const char * opt_object_class_str = NULL; static CK_OBJECT_CLASS opt_object_class = -1; static CK_BYTE opt_object_id[100], new_object_id[100]; static const char * opt_attr_from_file = NULL; static size_t opt_object_id_len = 0, new_object_id_len = 0; static char * opt_object_label = NULL; static const char * opt_pin = NULL; static const char * opt_so_pin = NULL; static const char * opt_puk = NULL; static const char * opt_new_pin = NULL; static char * opt_application_label = NULL; static char * opt_application_id = NULL; 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_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 void *module = 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[512]; int subject_len; unsigned char issuer[512]; int issuer_len; unsigned char serialnum[128]; int serialnum_len; }; struct rsakey_info { unsigned char *modulus; int modulus_len; unsigned char *public_exponent; int public_exponent_len; unsigned char *private_exponent; int private_exponent_len; unsigned char *prime_1; int prime_1_len; unsigned char *prime_2; int prime_2_len; unsigned char *exponent_1; int exponent_1_len; unsigned char *exponent_2; int exponent_2_len; unsigned char *coefficient; int coefficient_len; }; struct gostkey_info { struct sc_lv_data param_oid; struct sc_lv_data public; struct sc_lv_data private; }; 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); 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); static int change_pin(CK_SLOT_ID, CK_SESSION_HANDLE); static int unlock_pin(CK_SLOT_ID slot, CK_SESSION_HANDLE sess, int login_type); 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 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_id_or_label(CK_SESSION_HANDLE sess, CK_OBJECT_CLASS cls, 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_object_flags(CK_SESSION_HANDLE, uint16_t flags, CK_OBJECT_HANDLE_PTR, const unsigned char *, size_t id_len, 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 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); static CK_ULONG get_mechanisms(CK_SLOT_ID, CK_MECHANISM_TYPE_PTR *, CK_FLAGS); static void p11_fatal(const char *, CK_RV); 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 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 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 # define O_BINARY 0 #endif #define ATTR_METHOD(ATTR, TYPE) \ static TYPE \ get##ATTR(CK_SESSION_HANDLE sess, CK_OBJECT_HANDLE obj) \ { \ TYPE type = 0; \ CK_ATTRIBUTE attr = { CKA_##ATTR, &type, sizeof(type) }; \ CK_RV rv; \ \ rv = p11->C_GetAttributeValue(sess, obj, &attr, 1); \ if (rv != CKR_OK) \ p11_warn("C_GetAttributeValue(" #ATTR ")", rv); \ return type; \ } #define VARATTR_METHOD(ATTR, TYPE) \ static TYPE * \ get##ATTR(CK_SESSION_HANDLE sess, CK_OBJECT_HANDLE obj, CK_ULONG_PTR pulCount) \ { \ CK_ATTRIBUTE attr = { CKA_##ATTR, NULL, 0 }; \ CK_RV rv; \ if (pulCount) \ *pulCount = 0; \ rv = p11->C_GetAttributeValue(sess, obj, &attr, 1); \ if (rv == CKR_OK) { \ if (attr.ulValueLen == (CK_ULONG)(-1)) \ return NULL; \ if (!(attr.pValue = calloc(1, attr.ulValueLen + 1))) \ util_fatal("out of memory in get" #ATTR ": %m"); \ rv = p11->C_GetAttributeValue(sess, obj, &attr, 1); \ if (attr.ulValueLen == (CK_ULONG)(-1)) { \ free(attr.pValue); \ return NULL; \ } \ if (pulCount) \ *pulCount = attr.ulValueLen / sizeof(TYPE); \ } else if (rv != CKR_ATTRIBUTE_TYPE_INVALID) { \ p11_warn("C_GetAttributeValue(" #ATTR ")", rv); \ } \ return (TYPE *) attr.pValue; \ } /* * Define attribute accessors */ 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(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 */ 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(SUBJECT, unsigned char); /* getSUBJECT */ VARATTR_METHOD(SERIAL_NUMBER, unsigned char); /* getSERIAL_NUMBER */ VARATTR_METHOD(PUBLIC_EXPONENT, CK_BYTE); /* getPUBLIC_EXPONENT */ #endif 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 */ int main(int argc, char * argv[]) { CK_SESSION_HANDLE session = CK_INVALID_HANDLE; CK_OBJECT_HANDLE object = CK_INVALID_HANDLE; int err = 0, c, long_optind = 0; int do_show_info = 0; int do_list_slots = 0; 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; int do_gen_key = 0; int do_write_object = 0; int do_read_object = 0; int do_delete_object = 0; int do_set_id = 0; int do_test = 0; int do_test_kpgen_certwrite = 0; 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; int do_init_token = 0; int do_init_pin = 0; int do_change_pin = 0; int do_unlock_pin = 0; int action_count = 0; int do_generate_random = 0; char *s = NULL; CK_RV rv; #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 #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); if (c == -1) break; switch (c) { case 'I': do_show_info = 1; action_count++; break; case 'L': do_list_slots = 1; action_count++; break; case 'T': do_list_slots = 1; list_token_slots = 1; action_count++; break; case 'M': do_list_mechs = 1; action_count++; break; case 'O': need_session |= NEED_SESSION_RO; do_list_objects = 1; action_count++; break; case 'h': need_session |= NEED_SESSION_RO; do_hash = 1; action_count++; break; case 'k': need_session |= NEED_SESSION_RW; do_gen_keypair = 1; action_count++; break; case OPT_GENERATE_KEY: need_session |= NEED_SESSION_RW; do_gen_key = 1; action_count++; break; case 'w': need_session |= NEED_SESSION_RW; do_write_object = 1; opt_file_to_write = optarg; action_count++; break; case 'r': need_session |= NEED_SESSION_RO; do_read_object = 1; action_count++; break; case 'b': need_session |= NEED_SESSION_RW; do_delete_object = 1; action_count++; break; case 'e': need_session |= NEED_SESSION_RW; do_set_id = 1; new_object_id_len = sizeof(new_object_id); 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); } action_count++; break; case OPT_ATTR_FROM: opt_attr_from_file = optarg; break; case 'y': opt_object_class_str = optarg; if (strcmp(optarg, "cert") == 0) opt_object_class = CKO_CERTIFICATE; else if (strcmp(optarg, "privkey") == 0) opt_object_class = CKO_PRIVATE_KEY; else if (strcmp(optarg, "secrkey") == 0) opt_object_class = CKO_SECRET_KEY; else if (strcmp(optarg, "pubkey") == 0) opt_object_class = CKO_PUBLIC_KEY; else if (strcmp(optarg, "data") == 0) opt_object_class = CKO_DATA; else { fprintf(stderr, "Unsupported object type \"%s\"\n", optarg); util_print_usage_and_die(app_name, options, option_help, NULL); } break; case 'd': opt_object_id_len = sizeof(opt_object_id); 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); } break; case 'a': opt_object_label = optarg; break; case 'i': opt_input = optarg; break; 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_RO; opt_login = 1; util_get_pin(optarg, &opt_pin); break; case 'c': do_change_pin = 1; need_session |= NEED_SESSION_RW; action_count++; break; case OPT_UNLOCK_PIN: do_unlock_pin = 1; need_session |= NEED_SESSION_RW; action_count++; break; case 's': 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_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_RW; do_test = 1; action_count++; break; case 'z': do_test_kpgen_certwrite = 1; opt_file_to_write = optarg; action_count++; break; case 'v': verbose++; break; case OPT_SLOT: opt_slot = (CK_SLOT_ID) strtoul(optarg, NULL, 0); opt_slot_set = 1; if (verbose) fprintf(stderr, "Using slot with ID 0x%lx\n", opt_slot); break; case OPT_SLOT_DESCRIPTION: if (opt_slot_set) { 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-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-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; break; case OPT_MODULE: opt_module = optarg; break; case OPT_APPLICATION_LABEL: opt_application_label = optarg; break; case OPT_APPLICATION_ID: opt_application_id = optarg; break; case OPT_ISSUER: opt_issuer = optarg; break; case OPT_SUBJECT: opt_subject = optarg; break; case OPT_NEW_PIN: util_get_pin(optarg, &opt_new_pin); break; case OPT_PUK: util_get_pin(optarg, &opt_puk); break; case OPT_LOGIN_TYPE: if (!strcmp(optarg, "so")) opt_login_type = CKU_SO; else if (!strcmp(optarg, "user")) opt_login_type = CKU_USER; else if (!strcmp(optarg, "context-specific")) opt_login_type = CKU_CONTEXT_SPECIFIC; else { fprintf(stderr, "Unsupported login type \"%s\"\n", optarg); util_print_usage_and_die(app_name, options, option_help, NULL); } break; case OPT_SO_PIN: util_get_pin(optarg, &opt_so_pin); break; case OPT_INIT_TOKEN: do_init_token = 1; action_count++; break ; case OPT_INIT_PIN: need_session |= NEED_SESSION_RW; do_init_pin = 1; action_count++; break ; case OPT_KEY_TYPE: opt_key_type = optarg; break; case OPT_KEY_USAGE_SIGN: opt_key_usage_sign = 1; opt_key_usage_default = 0; break; case OPT_KEY_USAGE_DECRYPT: opt_key_usage_decrypt = 1; opt_key_usage_default = 0; break; case OPT_KEY_USAGE_DERIVE: 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_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_TEST_HOTPLUG: opt_test_hotplug = 1; action_count++; break; case OPT_TEST_EC: do_test_ec = 1; action_count++; break; case OPT_DERIVE_PASS_DER: opt_derive_pass_der = 1; /* fall through */ case OPT_DERIVE: need_session |= NEED_SESSION_RW; do_derive = 1; action_count++; break; #ifndef _WIN32 case OPT_TEST_FORK: 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; opt_random_bytes = strtoul(optarg, NULL, 0); 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; default: util_print_usage_and_die(app_name, options, option_help, NULL); } } if (optind < argc) { util_fatal("invalid option(s) given"); } if (action_count == 0) util_print_usage_and_die(app_name, options, option_help, NULL); #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; } /* 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"); #endif /* defined(_WIN32) || defined(HAVE_PTHREAD) */ if (rv != CKR_OK && rv != CKR_CRYPTOKI_ALREADY_INITIALIZED) p11_fatal("C_Initialize", rv); #ifndef _WIN32 if (do_test_fork) test_fork(); #endif if (do_show_info) show_cryptoki_info(); list_slots(list_token_slots, 1, do_list_slots); if (opt_test_hotplug) { test_card_detection(0); test_card_detection(1); } if (p11_num_slots == 0) { fprintf(stderr, "No slots.\n"); err = 1; goto end; } 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); err = 1; goto end; } if (verbose) fprintf(stderr, "Using slot with label \"%s\" (0x%lx)\n", opt_slot_description, opt_slot); } else if (opt_token_label) { if (!find_slot_by_token_label(opt_token_label, &opt_slot)) { fprintf(stderr, "No slot with token named \"%s\" found\n", opt_token_label); err = 1; goto end; } if (verbose) fprintf(stderr, "Using slot with label \"%s\" (0x%lx)\n", opt_slot_description, opt_slot); } else if (opt_slot_index_set) { if (opt_slot_index < p11_num_slots) { opt_slot = p11_slots[opt_slot_index]; 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-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"); err = 1; goto end; } } } if (do_list_mechs) list_mechs(opt_slot); 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)) util_fatal("Token not initialized"); if (info.flags & CKF_LOGIN_REQUIRED) opt_login++; } if (do_init_token) init_token(opt_slot); if (need_session) { int flags = CKF_SERIAL_SESSION; if (need_session & NEED_SESSION_RW) flags |= CKF_RW_SESSION; rv = p11->C_OpenSession(opt_slot, flags, NULL, NULL, &session); if (rv != CKR_OK) p11_fatal("C_OpenSession", rv); } if (opt_login) { int r; if (opt_login_type == -1) opt_login_type = do_init_pin ? CKU_SO : CKU_USER; r = login(session, opt_login_type); if (r != 0) return r; } if (do_change_pin) /* To be sure we won't mix things up with the -l or -p options, * we safely stop here. */ return change_pin(opt_slot, session); if (do_unlock_pin) { if (opt_login_type != -1 && opt_login_type != CKU_CONTEXT_SPECIFIC) { fprintf(stderr, "Invalid login type for 'Unlock User PIN' operation\n"); util_print_usage_and_die(app_name, options, option_help, NULL); } return unlock_pin(opt_slot, session, opt_login_type); } if (do_init_pin) { init_pin(opt_slot, session); /* We logged in as a CKU_SO user just to initialize * the User PIN, we now have to exit. */ goto end; } 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 */ find_object_flags(session, mf_flags, &object, opt_object_id_len ? opt_object_id : NULL, opt_object_id_len, 0); } else if (!find_object(session, CKO_PRIVATE_KEY, &object, opt_object_id_len ? opt_object_id : NULL, opt_object_id_len, 0)) if (!find_object(session, CKO_SECRET_KEY, &object, opt_object_id_len ? opt_object_id : NULL, opt_object_id_len, 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 */ find_object_flags(session, mf_flags, &object, opt_object_id_len ? opt_object_id : NULL, opt_object_id_len, 0); } else if (!find_object(session, CKO_PRIVATE_KEY, &object, opt_object_id_len ? opt_object_id : NULL, opt_object_id_len, 0)) if (!find_object(session, CKO_SECRET_KEY, &object, opt_object_id_len ? opt_object_id : NULL, opt_object_id_len, 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 */ find_object_flags(session, mf_flags, &object, opt_object_id_len ? opt_object_id : NULL, opt_object_id_len, 0); } else if (!find_object(session, CKO_PUBLIC_KEY, &object, opt_object_id_len ? opt_object_id : NULL, opt_object_id_len, 0)) if (!find_object(session, CKO_SECRET_KEY, &object, opt_object_id_len ? opt_object_id : NULL, opt_object_id_len, 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 */ find_object_flags(session, mf_flags, &object, opt_object_id_len ? opt_object_id : NULL, opt_object_id_len, 0); } else if (!find_object(session, CKO_PUBLIC_KEY, &object, opt_object_id_len ? opt_object_id : NULL, opt_object_id_len, 0) && !find_object(session, CKO_CERTIFICATE, &object, opt_object_id_len ? opt_object_id : NULL, opt_object_id_len, 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); 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); if (do_gen_keypair) { CK_OBJECT_HANDLE hPublicKey, hPrivateKey; gen_keypair(opt_slot, session, &hPublicKey, &hPrivateKey, opt_key_type); } if (do_gen_key) { CK_OBJECT_HANDLE hSecretKey; gen_key(opt_slot, session, &hSecretKey, opt_key_type, NULL); } if (do_write_object) { if (opt_object_class_str == NULL) util_fatal("You should specify the object type with the -y option"); write_object(session); } if (do_read_object) { if (opt_object_class_str == NULL) util_fatal("You should specify type of the object to read"); if (opt_object_id_len == 0 && opt_object_label == NULL && opt_application_label == NULL && opt_application_id == NULL && opt_issuer == NULL && opt_subject == NULL) util_fatal("You should specify at least one of the " "object ID, object label, application label or application ID"); read_object(session); } if (do_delete_object) { 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_object_index_set == 0) util_fatal("You should specify at least one of the " "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 && opt_object_label == NULL) util_fatal("You should specify the current object with the -d or -a option"); set_id_attr(session); } if (do_test) p11_test(session); if (do_test_kpgen_certwrite) { if (!opt_login) fprintf(stderr, "ERR: login required\n"); else session = test_kpgen_certwrite(opt_slot, session); } if (do_test_ec) { if (!opt_login) fprintf(stderr, "ERR: login required\n"); else test_ec(opt_slot, session); } if (do_generate_random) { generate_random(session); } end: if (session != CK_INVALID_HANDLE) { rv = p11->C_CloseSession(session); if (rv != CKR_OK) p11_fatal("C_CloseSession", rv); } #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); if (module) C_UnloadModule(module); return err; } static void show_cryptoki_info(void) { CK_INFO info; CK_RV rv; rv = p11->C_GetInfo(&info); if (rv != CKR_OK) p11_fatal("C_GetInfo", rv); printf("Cryptoki version %u.%u\n", info.cryptokiVersion.major, info.cryptokiVersion.minor); printf("Manufacturer %s\n", p11_utf8_to_local(info.manufacturerID, sizeof(info.manufacturerID))); printf("Library %s (ver %u.%u)\n", p11_utf8_to_local(info.libraryDescription, sizeof(info.libraryDescription)), info.libraryVersion.major, 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; CK_ULONG n; CK_RV rv; /* Get the list of slots */ if (refresh) { rv = p11->C_GetSlotList(tokens, NULL, &p11_num_slots); if (rv != CKR_OK) p11_fatal("C_GetSlotList(NULL)", rv); free(p11_slots); 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); } } if (!print) return; printf("Available slots:\n"); for (n = 0; n < p11_num_slots; n++) { printf("Slot %lu (0x%lx): ", n, p11_slots[n]); rv = p11->C_GetSlotInfo(p11_slots[n], &info); if (rv != CKR_OK) { printf("(GetSlotInfo failed, %s)\n", CKR2Str(rv)); continue; } printf("%s\n", p11_utf8_to_local(info.slotDescription, sizeof(info.slotDescription))); if ((!verbose) && !(info.flags & CKF_TOKEN_PRESENT)) { printf(" (empty)\n"); continue; } if (verbose) { printf(" manufacturer: %s\n", p11_utf8_to_local(info.manufacturerID, sizeof(info.manufacturerID))); printf(" hardware ver: %u.%u\n", info.hardwareVersion.major, info.hardwareVersion.minor); printf(" firmware ver: %u.%u\n", info.firmwareVersion.major, info.firmwareVersion.minor); printf(" flags: %s\n", p11_slot_info_flags(info.flags)); } if (info.flags & CKF_TOKEN_PRESENT) show_token(p11_slots[n]); } } 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) { 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); 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; CK_RV rv; rv = p11->C_GetTokenInfo(slot, &info); if (rv == CKR_TOKEN_NOT_RECOGNIZED) { printf(" (token not recognized)\n"); return; } else if (rv != CKR_OK) { printf("C_GetTokenInfo() failed: rv = %s\n", CKR2Str(rv)); return; } if (!(info.flags & CKF_TOKEN_INITIALIZED) && (!verbose)) { printf(" token state: uninitialized\n"); return; } printf(" token label : %s\n", p11_utf8_to_local(info.label, sizeof(info.label))); printf(" token manufacturer : %s\n", p11_utf8_to_local(info.manufacturerID, sizeof(info.manufacturerID))); printf(" token model : %s\n", p11_utf8_to_local(info.model, sizeof(info.model))); printf(" token flags : %s\n", p11_token_info_flags(info.flags)); printf(" hardware version : %d.%d\n", info.hardwareVersion.major, info.hardwareVersion.minor); printf(" firmware version : %d.%d\n", info.firmwareVersion.major, info.firmwareVersion.minor); 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", get_uri(&info)); printf("\n"); } static void list_mechs(CK_SLOT_ID slot) { CK_MECHANISM_TYPE *mechs = NULL; CK_ULONG n, num_mechs = 0; CK_RV rv; num_mechs = get_mechanisms(slot, &mechs, -1); printf("Supported mechanisms:\n"); for (n = 0; n < num_mechs; n++) { CK_MECHANISM_INFO info; printf(" %s", p11_mechanism_to_name(mechs[n])); rv = p11->C_GetMechanismInfo(slot, mechs[n], &info); if (rv == CKR_OK) { if (info.ulMinKeySize || info.ulMaxKeySize) { printf(", keySize={"); if (info.ulMinKeySize) printf("%li", info.ulMinKeySize); printf(","); if (info.ulMaxKeySize) printf("%li", info.ulMaxKeySize); printf("}"); } if (info.flags & CKF_HW) { printf(", hw"); info.flags &= ~CKF_HW; } if (info.flags & CKF_ENCRYPT) { printf(", encrypt"); info.flags &= ~CKF_ENCRYPT; } if (info.flags & CKF_DECRYPT) { printf(", decrypt"); info.flags &= ~CKF_DECRYPT; } if (info.flags & CKF_DIGEST) { printf(", digest"); info.flags &= ~CKF_DIGEST; } if (info.flags & CKF_SIGN) { printf(", sign"); info.flags &= ~CKF_SIGN; } if (info.flags & CKF_SIGN_RECOVER) { printf(", sign_recover"); info.flags &= ~CKF_SIGN_RECOVER; } if (info.flags & CKF_VERIFY) { printf(", verify"); info.flags &= ~CKF_VERIFY; } if (info.flags & CKF_VERIFY_RECOVER) { printf(", verify_recover"); info.flags &= ~CKF_VERIFY_RECOVER; } if (info.flags & CKF_GENERATE) { printf(", generate"); info.flags &= ~CKF_GENERATE; } if (info.flags & CKF_GENERATE_KEY_PAIR) { printf(", generate_key_pair"); info.flags &= ~CKF_GENERATE_KEY_PAIR; } if (info.flags & CKF_WRAP) { printf(", wrap"); info.flags &= ~CKF_WRAP; } if (info.flags & CKF_UNWRAP) { printf(", unwrap"); info.flags &= ~CKF_UNWRAP; } if (info.flags & CKF_DERIVE) { 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); } printf("\n"); } if (mechs) free(mechs); } static int login(CK_SESSION_HANDLE session, int login_type) { char *pin = NULL; size_t len = 0; int pin_allocated = 0, r; CK_TOKEN_INFO info; CK_RV rv; CK_FLAGS pin_flags; get_token_info(opt_slot, &info); /* Identify which pin to enter */ if (login_type == CKU_SO) pin = (char *) opt_so_pin; else if (login_type == CKU_USER) pin = (char *) opt_pin; else if (login_type == CKU_CONTEXT_SPECIFIC) pin = opt_pin ? (char *) opt_pin : (char *) opt_puk; if (!pin && !(info.flags & CKF_PROTECTED_AUTHENTICATION_PATH)) { printf("Logging in to \"%s\".\n", p11_utf8_to_local(info.label, sizeof(info.label))); if (login_type == CKU_SO) { pin_flags=info.flags & ( CKF_SO_PIN_COUNT_LOW | CKF_SO_PIN_FINAL_TRY | CKF_SO_PIN_LOCKED | CKF_SO_PIN_TO_BE_CHANGED); if(pin_flags) printf("WARNING: %s\n",p11_token_info_flags(pin_flags)); printf("Please enter SO PIN: "); } else if (login_type == CKU_USER) { pin_flags=info.flags & ( CKF_USER_PIN_COUNT_LOW | CKF_USER_PIN_FINAL_TRY | CKF_USER_PIN_LOCKED | CKF_USER_PIN_TO_BE_CHANGED); if(pin_flags) printf("WARNING: %s\n",p11_token_info_flags(pin_flags)); printf("Please enter User PIN: "); } else if (login_type == CKU_CONTEXT_SPECIFIC) { printf("Please enter context specific PIN: "); } r = util_getpass(&pin, &len, stdin); if (r < 0) util_fatal("util_getpass error"); pin_allocated = 1; } if (!(info.flags & CKF_PROTECTED_AUTHENTICATION_PATH) && (!pin || !*pin) && login_type != CKU_CONTEXT_SPECIFIC) return 1; rv = p11->C_Login(session, login_type, (CK_UTF8CHAR *) pin, pin == NULL ? 0 : strlen(pin)); if (rv != CKR_OK && rv != CKR_USER_ALREADY_LOGGED_IN) p11_fatal("C_Login", rv); if (pin_allocated) free(pin); return 0; } static void init_token(CK_SLOT_ID slot) { unsigned char token_label[33]; char new_buf[21], *new_pin = NULL; size_t len = 0; int pin_allocated = 0, r; CK_TOKEN_INFO info; CK_RV rv; if (!opt_object_label) util_fatal("The token label must be specified using --label"); snprintf((char *) token_label, sizeof (token_label), "%-32.32s", opt_object_label); get_token_info(slot, &info); if (opt_so_pin != NULL) { new_pin = (char *) opt_so_pin; } else { if (!(info.flags & CKF_PROTECTED_AUTHENTICATION_PATH)) { printf("Please enter the new SO PIN: "); r = util_getpass(&new_pin, &len, stdin); if (r < 0) util_fatal("No PIN entered"); if (!new_pin || !*new_pin || strlen(new_pin) > 20) util_fatal("Invalid SO PIN"); strlcpy(new_buf, new_pin, sizeof new_buf); free(new_pin); new_pin = NULL; printf("Please enter the new SO PIN (again): "); r = util_getpass(&new_pin, &len, stdin); if (r < 0) util_fatal("No PIN entered"); if (!new_pin || !*new_pin || strcmp(new_buf, new_pin) != 0) util_fatal("Different new SO PINs"); pin_allocated = 1; } } rv = p11->C_InitToken(slot, (CK_UTF8CHAR *) new_pin, new_pin == NULL ? 0 : strlen(new_pin), token_label); if (rv != CKR_OK) p11_fatal("C_InitToken", rv); printf("Token successfully initialized\n"); if (pin_allocated) free(new_pin); } static void init_pin(CK_SLOT_ID slot, CK_SESSION_HANDLE sess) { char *pin; char *new_pin1 = NULL, *new_pin2 = NULL; size_t len1 = 0, len2 = 0; int r; CK_TOKEN_INFO info; CK_RV rv; get_token_info(slot, &info); if (!(info.flags & CKF_PROTECTED_AUTHENTICATION_PATH)) { if (! opt_pin && !opt_new_pin) { printf("Please enter the new PIN: "); r = util_getpass(&new_pin1,&len1,stdin); if (r < 0) util_fatal("No PIN entered"); if (!new_pin1 || !*new_pin1 || strlen(new_pin1) > 20) util_fatal("Invalid User PIN"); printf("Please enter the new PIN again: "); r = util_getpass(&new_pin2, &len2, stdin); if (r < 0) util_fatal("No PIN entered"); if (!new_pin2 || !*new_pin2 || strcmp(new_pin1, new_pin2) != 0) util_fatal("Different new User PINs"); } } pin = (char *) opt_pin; if (!pin) pin = (char *) opt_new_pin; if (!pin) pin = new_pin1; rv = p11->C_InitPIN(sess, (CK_UTF8CHAR *) pin, pin == NULL ? 0 : strlen(pin)); if (new_pin1) { memset(new_pin1, 0, len1); free(new_pin1); } if (new_pin2) { memset(new_pin2,0, len2); free(new_pin2); } if (rv != CKR_OK) p11_fatal("C_InitPIN", rv); printf("User PIN successfully initialized\n"); } static int change_pin(CK_SLOT_ID slot, CK_SESSION_HANDLE sess) { 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 (!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; } } rv = p11->C_SetPIN(sess, (CK_UTF8CHAR *) old_pin, old_pin == NULL ? 0 : strlen(old_pin), (CK_UTF8CHAR *) new_pin, new_pin == NULL ? 0 : strlen(new_pin)); if (rv != CKR_OK) p11_fatal("C_SetPIN", rv); printf("PIN successfully changed\n"); return 0; } static int unlock_pin(CK_SLOT_ID slot, CK_SESSION_HANDLE sess, int login_type) { char unlock_buf[21], *unlock_code = NULL; char new_buf[21], *new_pin = NULL; CK_TOKEN_INFO info; CK_RV rv; int r; size_t len = 0; get_token_info(slot, &info); if (login_type == CKU_CONTEXT_SPECIFIC) unlock_code = opt_pin ? (char *) opt_pin : (char *) opt_puk; else if (login_type == -1) unlock_code = (char *) opt_puk; else return 1; if (!(info.flags & CKF_PROTECTED_AUTHENTICATION_PATH) && !unlock_code) { if (login_type == CKU_CONTEXT_SPECIFIC) printf("Please enter the 'Change PIN' context secret code: "); else if (login_type == -1) printf("Please enter unblock code for User PIN: "); r = util_getpass(&unlock_code, &len, stdin); if (r < 0) return 1; if (!unlock_code || !*unlock_code || strlen(unlock_code) > 20) return 1; strcpy(unlock_buf, unlock_code); unlock_code = unlock_buf; } new_pin = (char *) opt_new_pin; if (!(info.flags & CKF_PROTECTED_AUTHENTICATION_PATH) && !new_pin) { printf("Please enter the new PIN: "); r = util_getpass(&new_pin, &len, stdin); if (r < 0) return 1; strlcpy(new_buf, new_pin, sizeof new_buf); 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) { if (new_pin != opt_new_pin) free(new_pin); printf(" different new PINs, exiting\n"); return -1; } if (!new_pin || !*new_pin || strlen(new_pin) > 20) { if (new_pin != opt_new_pin) free(new_pin); return 1; } } rv = p11->C_SetPIN(sess, (CK_UTF8CHAR *) unlock_code, unlock_code == NULL ? 0 : strlen(unlock_code), (CK_UTF8CHAR *) new_pin, new_pin == NULL ? 0 : strlen(new_pin)); if (rv != CKR_OK) p11_fatal("C_SetPIN", rv); printf("PIN successfully changed\n"); return 0; } /* 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) { char ecpbuf[64]; if (ec_params_size > (sizeof(ecpbuf) / 2)) { util_fatal("Invalid EC params"); } sc_bin_to_hex(ec_params, ec_params_size, ecpbuf, sizeof(ecpbuf), 0); for (size_t i = 0; ec_curve_infos[i].name != NULL; ++i) { if (strcmp(ec_curve_infos[i].ec_params, ecpbuf) == 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 = (get_private_key_length(session, key) + 7) / 8; 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]; 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_RV rv; CK_ULONG sig_len; int fd; ssize_t sz; unsigned long hashlen; if (!opt_mechanism_used) 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); /* support pure EdDSA only */ if (opt_mechanism == CKM_EDDSA) { 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); } } 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); if (opt_mechanism == CKM_RSA_PKCS_PSS && (size_t)sz != hashlen) { util_fatal("For %s mechanism, message size (got %z bytes) " "must be equal to specified digest length (%lu)\n", p11_mechanism_to_name(opt_mechanism), sz, hashlen); } else if (opt_mechanism == 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); } rv = CKR_CANCEL; if ((size_t)sz < sizeof(in_buffer)) { rv = p11->C_SignInit(session, &mech, key); if (rv != CKR_OK) p11_fatal("C_SignInit", rv); 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, 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 ((getCLASS(session, key) == CKO_PRIVATE_KEY) && getALWAYS_AUTHENTICATE(session, key)) login(session,CKU_CONTEXT_SPECIFIC); do { rv = p11->C_SignUpdate(session, in_buffer, sz); if (rv != CKR_OK) p11_fatal("C_SignUpdate", rv); sz = read(fd, in_buffer, sizeof(in_buffer)); } while (sz > 0); sig_len = sizeof(sig_buffer); rv = p11->C_SignFinal(session, sig_buffer, &sig_len); if (rv != CKR_OK) p11_fatal("C_SignFinal", rv); } if (fd != 0) close(fd); 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); } 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)) { util_fatal("Failed to convert signature to ASN.1 sequence format"); } memcpy(sig_buffer, seq, seqlen); sig_len = seqlen; free(seq); } } sz = write(fd, sig_buffer, sig_len); if (sz < 0) util_fatal("Failed to write to %s: %m", opt_output); if (fd != 1) close(fd); } static void verify_signature(CK_SLOT_ID slot, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE key) { unsigned char in_buffer[1025], sig_buffer[512]; 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_RV rv; CK_ULONG sig_len; int fd, fd2; 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"); } } /* support pure EdDSA only */ if (opt_mechanism == CKM_EDDSA) { 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); } } /* 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); sz2 = read(fd2, sig_buffer, sizeof(sig_buffer)); if (sz2 < 0) util_fatal("Cannot read from %s: %m", opt_signature_file); close(fd2); 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"))) { CK_BYTE* bytes; CK_ULONG len; size_t rs_len = 0; unsigned char rs_buffer[512]; bytes = getEC_POINT(session, key, &len); free(bytes); /* * (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" * Do simple size calculation based on DER encoding */ if ((len - 2) <= 127) rs_len = len - 3; else if ((len - 3) <= 255) rs_len = len - 4; else util_fatal("Key not supported"); 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; } } /* 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); if (opt_mechanism == CKM_RSA_PKCS_PSS && (size_t)sz != hashlen) { util_fatal("For %s mechanism, message size (got %z bytes)" " must be equal to specified digest length (%lu)\n", p11_mechanism_to_name(opt_mechanism), sz, hashlen); } else if (opt_mechanism == 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); } 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); } 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: printf("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 { printf("mgf not set, defaulting to %s\n", p11_mgf_to_name(oaep_params->mgf)); } oaep_params->source = CKZ_DATA_SPECIFIED; oaep_params->pSourceData = param; oaep_params->ulSourceDataLen = param_len; mech->pParameter = oaep_params; mech->ulParameterLen = sizeof(*oaep_params); printf("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 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; CK_RSA_PKCS_OAEP_PARAMS oaep_params; CK_GCM_PARAMS gcm_params = {0}; CK_ULONG in_len, out_len; int fd_in, fd_out; CK_BYTE_PTR iv = NULL; size_t iv_size = 0; ssize_t sz; CK_BYTE_PTR aad = NULL; size_t aad_size = 0; if (!opt_mechanism_used) 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"); /* set "default" MGF and hash algorithms. We can overwrite MGF later */ switch (opt_mechanism) { case CKM_RSA_PKCS_OAEP: build_rsa_oaep_params(&oaep_params, &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"); gcm_params.pIv = iv; gcm_params.ulIvLen = iv_size; aad = hex_string_to_byte_array(opt_aad, &aad_size, "AAD"); gcm_params.pAAD = aad; gcm_params.ulAADLen = aad_size; gcm_params.ulTagBits = opt_tag_bits; mech.pParameter = &gcm_params; mech.ulParameterLen = sizeof(gcm_params); break; default: util_fatal("Mechanism %s illegal or not supported\n", p11_mechanism_to_name(opt_mechanism)); } 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); } sz = read(fd_in, in_buffer, sizeof(in_buffer)); in_len = sz; if (sz < 0) util_fatal("Cannot read from %s: %m", opt_input); 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; CK_RSA_PKCS_OAEP_PARAMS oaep_params; CK_ULONG in_len, out_len; int fd_in, fd_out; ssize_t sz; CK_GCM_PARAMS gcm_params = {0}; CK_BYTE_PTR iv = NULL; size_t iv_size = 0; CK_BYTE_PTR aad = NULL; size_t aad_size = 0; 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"); switch (opt_mechanism) { case CKM_RSA_PKCS_OAEP: build_rsa_oaep_params(&oaep_params, &mech, NULL, 0); break; 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"); gcm_params.pIv = iv; gcm_params.ulIvLen = iv_size; aad = hex_string_to_byte_array(opt_aad, &aad_size, "AAD"); gcm_params.pAAD = aad; gcm_params.ulAADLen = aad_size; gcm_params.ulTagBits = opt_tag_bits; mech.pParameter = &gcm_params; mech.ulParameterLen = sizeof(gcm_params); break; default: util_fatal("Mechanism %s illegal or not supported\n", p11_mechanism_to_name(opt_mechanism)); } 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); } sz = read(fd_in, in_buffer, sizeof(in_buffer)); in_len = sz; if (sz < 0) util_fatal("Cannot read from %s: %m", opt_input); 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); } 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); free(iv); free(aad); } static void hash_data(CK_SLOT_ID slot, CK_SESSION_HANDLE session) { unsigned char buffer[64]; CK_MECHANISM mech; CK_RV rv; CK_ULONG hash_len; int fd; ssize_t sz; if (!opt_mechanism_used) if (!find_mechanism(slot, CKF_DIGEST, NULL, 0, &opt_mechanism)) util_fatal("Digest mechanism is not supported"); fprintf(stderr, "Using digest algorithm %s\n", p11_mechanism_to_name(opt_mechanism)); memset(&mech, 0, sizeof(mech)); mech.mechanism = opt_mechanism; rv = p11->C_DigestInit(session, &mech); if (rv != CKR_OK) p11_fatal("C_DigestInit", rv); 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); while ((sz = read(fd, buffer, sizeof(buffer))) > 0) { rv = p11->C_DigestUpdate(session, buffer, sz); if (rv != CKR_OK) p11_fatal("C_DigestUpdate", rv); } if (fd != 0) close(fd); hash_len = sizeof(buffer); rv = p11->C_DigestFinal(session, buffer, &hash_len); if (rv != CKR_OK) p11_fatal("C_DigestFinal", rv); 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, buffer, hash_len); if (sz < 0) util_fatal("Failed to write to %s: %m", opt_output); if (fd != 1) close(fd); } #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) { CK_MECHANISM mechanism = {CKM_RSA_PKCS_KEY_PAIR_GEN, NULL_PTR, 0}; CK_ULONG modulusBits = 1024; CK_BYTE publicExponent[] = { 0x01, 0x00, 0x01 }; /* 65537 in bytes */ CK_BBOOL _true = TRUE; CK_BBOOL _false = FALSE; CK_OBJECT_CLASS pubkey_class = CKO_PUBLIC_KEY; CK_OBJECT_CLASS privkey_class = CKO_PRIVATE_KEY; CK_ATTRIBUTE publicKeyTemplate[20] = { {CKA_CLASS, &pubkey_class, sizeof(pubkey_class)}, {CKA_TOKEN, &_true, sizeof(_true)}, }; int n_pubkey_attr = 2; CK_ATTRIBUTE privateKeyTemplate[20] = { {CKA_CLASS, &privkey_class, sizeof(privkey_class)}, {CKA_TOKEN, &_true, sizeof(_true)}, {CKA_PRIVATE, &_true, sizeof(_true)}, {CKA_SENSITIVE, &_true, sizeof(_true)}, }; unsigned long int gost_key_type = -1; 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) { if (strncmp(type, "RSA:", strlen("RSA:")) == 0 || strncmp(type, "rsa:", strlen("rsa:")) == 0) { CK_MECHANISM_TYPE mtypes[] = {CKM_RSA_PKCS_KEY_PAIR_GEN, CKM_RSA_X9_31_KEY_PAIR_GEN}; size_t mtypes_num = sizeof(mtypes)/sizeof(mtypes[0]); CK_ULONG key_length; const char *size = type + strlen("RSA:"); if (!opt_mechanism_used) if (!find_mechanism(slot, CKF_GENERATE_KEY_PAIR, mtypes, mtypes_num, &opt_mechanism)) util_fatal("Generate RSA mechanism not supported"); key_length = (unsigned long)atol(size); 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++; FILL_ATTR(publicKeyTemplate[n_pubkey_attr], CKA_PUBLIC_EXPONENT, publicExponent, sizeof(publicExponent)); 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++; } if (opt_key_usage_default || 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++; } 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_KEY_TYPE, &key_type, sizeof(key_type)); n_privkey_attr++; } 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; key_type = CKK_EC; for (ii=0; ec_curve_infos[ii].name; ii++) { if (!strcmp(ec_curve_infos[ii].name, type + 3)) break; if (!strcmp(ec_curve_infos[ii].oid, type + 3)) break; } 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); } 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 EC key mechanism %lx not supported", mtypes[0]); } } ecparams_size = strlen(ec_curve_infos[ii].ec_params) / 2; ecparams = malloc(ecparams_size); if (!ecparams) util_fatal("Allocation error", 0); if (sc_hex_to_bin(ec_curve_infos[ii].ec_params, ecparams, &ecparams_size)) { fprintf(stderr, "Cannot convert \"%s\"\n", ec_curve_infos[ii].ec_params); util_print_usage_and_die(app_name, options, option_help, NULL); } 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++; } if (opt_key_usage_default || opt_key_usage_derive) { FILL_ATTR(publicKeyTemplate[n_pubkey_attr], CKA_DERIVE, &_true, sizeof(_true)); n_pubkey_attr++; FILL_ATTR(privateKeyTemplate[n_privkey_attr], CKA_DERIVE, &_true, sizeof(_true)); n_privkey_attr++; } FILL_ATTR(publicKeyTemplate[n_pubkey_attr], CKA_EC_PARAMS, ecparams, ecparams_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) { 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"); 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; } 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("-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("-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 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.value, key_paramset_encoded_oid.len); n_pubkey_attr++; 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.value, hash_paramset_encoded_oid.len); n_pubkey_attr++; 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)); n_pubkey_attr++; FILL_ATTR(privateKeyTemplate[n_privkey_attr], CKA_KEY_TYPE, &gost_key_type, sizeof(gost_key_type)); n_privkey_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++; } /* do not set 'derive' attribute unless it is specified directly */ if (opt_key_usage_derive) { FILL_ATTR(privateKeyTemplate[n_privkey_attr], CKA_DERIVE, &_true, sizeof(_true)); n_privkey_attr++; } } else { util_fatal("Unknown key pair type %s", type); } mechanism.mechanism = opt_mechanism; } if (opt_object_label != NULL) { FILL_ATTR(publicKeyTemplate[n_pubkey_attr], CKA_LABEL, opt_object_label, strlen(opt_object_label)); FILL_ATTR(privateKeyTemplate[n_privkey_attr], CKA_LABEL, opt_object_label, strlen(opt_object_label)); n_pubkey_attr++; n_privkey_attr++; } if (opt_object_id_len != 0) { FILL_ATTR(publicKeyTemplate[n_pubkey_attr], CKA_ID, opt_object_id, opt_object_id_len); FILL_ATTR(privateKeyTemplate[n_privkey_attr], CKA_ID, opt_object_id, opt_object_id_len); n_pubkey_attr++; n_privkey_attr++; } if (opt_is_private != 0) { FILL_ATTR(publicKeyTemplate[n_pubkey_attr], CKA_PRIVATE, &_true, sizeof(_true)); n_pubkey_attr++; } else { FILL_ATTR(publicKeyTemplate[n_pubkey_attr], CKA_PRIVATE, &_false, sizeof(_false)); 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(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, hPublicKey, hPrivateKey); 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); 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) { 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] = { {CKA_CLASS, &secret_key_class, sizeof(secret_key_class)}, {CKA_TOKEN, &_true, sizeof(_true)}, }; int n_attr = 2; CK_RV rv; if (type != NULL) { if (strncasecmp(type, "AES:", strlen("AES:")) == 0) { CK_MECHANISM_TYPE mtypes[] = {CKM_AES_KEY_GEN}; size_t mtypes_num = sizeof(mtypes)/sizeof(mtypes[0]); const char *size = type + strlen("AES:"); key_type = CKK_AES; 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 AES:", type); FILL_ATTR(keyTemplate[n_attr], CKA_KEY_TYPE, &key_type, sizeof(key_type)); n_attr++; } 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]); const char *size = type + strlen("DES:"); key_type = CKK_DES; 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 DES:", type); FILL_ATTR(keyTemplate[n_attr], CKA_KEY_TYPE, &key_type, sizeof(key_type)); n_attr++; } 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]); const char *size = type + strlen("DES3:"); key_type = CKK_DES3; 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 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 { 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 { 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_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[2048]; CK_ULONG wrapped_key_length; CK_BYTE_PTR pWrappedKey; CK_GCM_PARAMS gcm_params = {0}; CK_BYTE_PTR iv = NULL; size_t iv_size = 0; CK_BYTE_PTR aad = NULL; size_t aad_size = 0; 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, 0)) if (!find_object(session, CKO_SECRET_KEY, &hUnwrappingKey, opt_object_id_len ? opt_object_id : NULL, opt_object_id_len, 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; switch (opt_mechanism) { case CKM_AES_CBC: case CKM_AES_CBC_PAD: iv_size = 16; iv = hex_string_to_byte_array(opt_iv, &iv_size, "IV"); mechanism.pParameter = iv; mechanism.ulParameterLen = iv_size; break; case CKM_AES_GCM: iv = hex_string_to_byte_array(opt_iv, &iv_size, "IV"); gcm_params.pIv = iv; gcm_params.ulIvLen = iv_size; aad = hex_string_to_byte_array(opt_aad, &aad_size, "AAD"); gcm_params.pAAD = aad; gcm_params.ulAADLen = aad_size; gcm_params.ulTagBits = opt_tag_bits; mechanism.pParameter = &gcm_params; mechanism.ulParameterLen = sizeof(gcm_params); break; default: // Nothing to do with other mechanisms. break; } 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); } 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++; } 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++; } 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++; } 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++; /* 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++; } if (opt_application_label != NULL) { FILL_ATTR(keyTemplate[n_attr], CKA_LABEL, opt_application_label, strlen(opt_application_label)); 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++; } } 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_UnwrapKey", rv); 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; CK_GCM_PARAMS gcm_params = {0}; CK_BYTE_PTR iv = NULL; size_t iv_size = 0; CK_BYTE_PTR aad = NULL; size_t aad_size = 0; 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; switch (opt_mechanism) { case CKM_AES_CBC: case CKM_AES_CBC_PAD: iv_size = 16; iv = hex_string_to_byte_array(opt_iv, &iv_size, "IV"); mechanism.pParameter = iv; mechanism.ulParameterLen = iv_size; break; case CKM_AES_GCM: iv = hex_string_to_byte_array(opt_iv, &iv_size, "IV"); gcm_params.pIv = iv; gcm_params.ulIvLen = iv_size; aad = hex_string_to_byte_array(opt_aad, &aad_size, "AAD"); gcm_params.pAAD = aad; gcm_params.ulAADLen = aad_size; gcm_params.ulTagBits = opt_tag_bits; mechanism.pParameter = &gcm_params; mechanism.ulParameterLen = sizeof(gcm_params); break; default: // Nothing to do with other mechanisms. break; } 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, 0)) if (!find_object(session, CKO_PRIVATE_KEY, &hkey, hkey_id_len ? hkey_id : NULL, hkey_id_len, 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, 0)) if (!find_object(session, CKO_SECRET_KEY, &hWrappingKey, opt_object_id_len ? opt_object_id : NULL, opt_object_id_len, 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, 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, (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) { unsigned char *contents_pointer = contents; *contents_len = i2d_X509(x, &contents_pointer); if (*contents_len < 0) util_fatal("Failed to convert PEM to DER"); } BIO_free(mem); } else { x = d2i_X509(NULL, (const unsigned char **)&data, len); } if (!x) { util_fatal("OpenSSL error during X509 certificate parsing"); } /* convert only (if needed) */ if (cert == NULL) return; /* check length first */ n = i2d_X509_NAME(X509_get_subject_name(x), NULL); if (n < 0) util_fatal("OpenSSL error while encoding subject name"); if (n > (int)sizeof (cert->subject)) util_fatal("subject name too long"); /* green light, actually do it */ p = cert->subject; n = i2d_X509_NAME(X509_get_subject_name(x), &p); cert->subject_len = n; /* check length first */ n = i2d_X509_NAME(X509_get_issuer_name(x), NULL); if (n < 0) util_fatal("OpenSSL error while encoding issuer name"); if (n > (int)sizeof (cert->issuer)) util_fatal("issuer name too long"); /* green light, actually do it */ p = cert->issuer; n = i2d_X509_NAME(X509_get_issuer_name(x), &p); cert->issuer_len = n; /* check length first */ n = i2d_ASN1_INTEGER(X509_get_serialNumber(x), NULL); if (n < 0) util_fatal("OpenSSL error while encoding serial number"); if (n > (int)sizeof (cert->serialnum)) util_fatal("serial number too long"); /* green light, actually do it */ 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, (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); if (*key == NULL) return -1; return 0; } #define RSA_GET_BN(RSA, LOCALNAME, BNVALUE) \ do { \ if (BNVALUE) { \ RSA->LOCALNAME = malloc(BN_num_bytes(BNVALUE)); \ if (!RSA->LOCALNAME) \ util_fatal("malloc() failure\n"); \ RSA->LOCALNAME##_len = BN_bn2bin(BNVALUE, RSA->LOCALNAME); \ } else { \ RSA->LOCALNAME##_len = 0; \ RSA->LOCALNAME = NULL; \ } \ } while (0) 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) { 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_GET_BN(rsa, prime_1, r_p); RSA_GET_BN(rsa, prime_2, r_q); 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 !defined(OPENSSL_NO_EC) static int parse_gost_pkey(EVP_PKEY *pkey, int private, struct gostkey_info *gost) { unsigned char *pder; BIGNUM *X, *Y; 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 = OBJ_txt2nid(name); #endif rv = i2d_ASN1_OBJECT(OBJ_nid2obj(nid), NULL); if (rv < 0) return -1; gost->param_oid.value = malloc(rv); if (!gost->param_oid.value) return -1; pder = gost->param_oid.value; rv = i2d_ASN1_OBJECT(OBJ_nid2obj(nid), &pder); gost->param_oid.len = rv; if (private) { #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) { #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 = 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; } #endif rv = -1; 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); if (!gost->public.value) rv = -1; else { BN_bn2bin(Y, gost->public.value); BN_bn2bin(X, gost->public.value + BN_num_bytes(Y)); } } 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) { #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((EC_KEY *)src, &gost->param_oid.value); #else BIGNUM *bignum = NULL; gost->param_oid.len = i2d_KeyParams(pkey, &gost->param_oid.value); #endif if (gost->param_oid.len <= 0) { return -1; } if (private) { #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) { #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 { unsigned char buf[512], *point; 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); #else EVP_PKEY_get_octet_string_param(pkey, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, buf, sizeof(buf), &point_len); #endif gost->public.value = malloc(MAX_HEADER_LEN+point_len); if (!gost->public.value) return -1; point = gost->public.value; ASN1_put_object(&point, 0, (int)point_len, V_ASN1_OCTET_STRING, V_ASN1_UNIVERSAL); header_len = point-gost->public.value; memcpy(point, buf, point_len); gost->public.len = header_len+point_len; #ifdef 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; #endif } return 0; } static int parse_ed_pkey(EVP_PKEY *pkey, int pk_type, int private, struct gostkey_info *gost) { static unsigned char ec_params_ed25519[] = {0x06, 0x03, 0x2b, 0x65, 0x70}; static unsigned char ec_params_ed448[] = {0x06, 0x03, 0x2b, 0x65, 0x71}; unsigned char *ec_params = (pk_type == EVP_PKEY_ED25519) ? ec_params_ed25519 : ec_params_ed448; size_t ec_params_size = (pk_type == EVP_PKEY_ED25519) ? sizeof(ec_params_ed25519) : sizeof(ec_params_ed448); unsigned char *key; size_t key_size; /* set EC_PARAMS value */ gost->param_oid.value = OPENSSL_malloc(ec_params_size); if (gost->param_oid.value == NULL) { return -1; } gost->param_oid.len = ec_params_size; memcpy(gost->param_oid.value, ec_params, 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 gost_info_free(struct gostkey_info gost) { OPENSSL_free(gost.param_oid.value); OPENSSL_free(gost.public.value); OPENSSL_free(gost.private.value); } #endif /* Currently for certificates (-type cert), private keys (-type privkey), public keys (-type pubkey) and data objects (-type data). */ static CK_RV write_object(CK_SESSION_HANDLE session) { CK_BBOOL _true = TRUE; CK_BBOOL _false = FALSE; 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, 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_RSA; size_t ret = 0; #ifdef ENABLE_OPENSSL struct x509cert_info cert; struct rsakey_info rsa; struct gostkey_info gost; EVP_PKEY *evp_key = NULL; int pk_type; memset(&cert, 0, sizeof(cert)); memset(&rsa, 0, sizeof(rsa)); memset(&gost, 0, sizeof(gost)); #endif f = fopen(opt_file_to_write, "rb"); if (f == NULL) util_fatal("Couldn't open file \"%s\"", opt_file_to_write); 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) { f = fopen(opt_attr_from_file, "rb"); if (f == NULL) util_fatal("Couldn't open file \"%s\"", opt_attr_from_file); 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) { if (opt_attr_from_file) { /* Convert contents from PEM to DER if needed * certdata already read and will be validated later */ #ifdef ENABLE_OPENSSL parse_certificate(NULL, contents, contents_len, contents, &contents_len); #else util_fatal("No OpenSSL support, cannot parse certificate"); #endif } else { 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; } } if (need_to_parse_certdata) { #ifdef ENABLE_OPENSSL /* Validate and get the certificate fields (from certdata) * and convert PEM to DER if needed */ parse_certificate(&cert, certdata, certdata_len, (opt_attr_from_file ? NULL : contents), &contents_len); #else util_fatal("No OpenSSL support, cannot parse certificate"); #endif } if (opt_object_class == CKO_PRIVATE_KEY || opt_object_class == CKO_PUBLIC_KEY) { #ifdef ENABLE_OPENSSL int is_private = opt_object_class == CKO_PRIVATE_KEY; int rv; rv = do_read_key(contents, contents_len, is_private, &evp_key); if (rv) { if (is_private) util_fatal("Cannot read private key"); else util_fatal("Cannot read public key"); } pk_type = EVP_PKEY_base_id(evp_key); if (pk_type == EVP_PKEY_RSA) { rv = parse_rsa_pkey(evp_key, is_private, &rsa); } #if !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; #ifdef EVP_PKEY_ED448 } else if ((pk_type == EVP_PKEY_ED25519) || (pk_type == EVP_PKEY_ED448)) { #else } else if (pk_type == EVP_PKEY_ED25519) { #endif rv = parse_ed_pkey(evp_key, pk_type, is_private, &gost); type = CKK_EC_EDWARDS; } #endif else util_fatal("Unsupported key type: 0x%X", pk_type); if (rv) util_fatal("Cannot parse key"); #else util_fatal("No OpenSSL support, cannot parse key"); #endif } switch(opt_object_class) { case CKO_CERTIFICATE: clazz = CKO_CERTIFICATE; cert_type = CKC_X_509; FILL_ATTR(cert_templ[0], CKA_TOKEN, &_true, sizeof(_true)); 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)); 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) { FILL_ATTR(cert_templ[n_cert_attr], CKA_LABEL, opt_object_label, strlen(opt_object_label)); n_cert_attr++; } if (opt_object_id_len != 0) { 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); n_cert_attr++; FILL_ATTR(cert_templ[n_cert_attr], CKA_ISSUER, cert.issuer, cert.issuer_len); n_cert_attr++; FILL_ATTR(cert_templ[n_cert_attr], CKA_SERIAL_NUMBER, cert.serialnum, cert.serialnum_len); n_cert_attr++; #endif break; case CKO_PRIVATE_KEY: clazz = CKO_PRIVATE_KEY; n_privkey_attr = 0; FILL_ATTR(privkey_templ[n_privkey_attr], CKA_CLASS, &clazz, sizeof(clazz)); n_privkey_attr++; FILL_ATTR(privkey_templ[n_privkey_attr], CKA_TOKEN, &_true, sizeof(_true)); n_privkey_attr++; FILL_ATTR(privkey_templ[n_privkey_attr], CKA_PRIVATE, &_true, sizeof(_true)); 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++; } if (opt_object_id_len != 0) { FILL_ATTR(privkey_templ[n_privkey_attr], CKA_ID, opt_object_id, opt_object_id_len); n_privkey_attr++; } if (opt_key_usage_sign != 0) { FILL_ATTR(privkey_templ[n_privkey_attr], CKA_SIGN, &_true, sizeof(_true)); n_privkey_attr++; } if (opt_key_usage_decrypt != 0) { 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)); 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; 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); n_privkey_attr++; FILL_ATTR(privkey_templ[n_privkey_attr], CKA_PUBLIC_EXPONENT, rsa.public_exponent, rsa.public_exponent_len); n_privkey_attr++; FILL_ATTR(privkey_templ[n_privkey_attr], CKA_PRIVATE_EXPONENT, rsa.private_exponent, rsa.private_exponent_len); n_privkey_attr++; FILL_ATTR(privkey_templ[n_privkey_attr], CKA_PRIME_1, rsa.prime_1, rsa.prime_1_len); n_privkey_attr++; FILL_ATTR(privkey_templ[n_privkey_attr], CKA_PRIME_2, rsa.prime_2, rsa.prime_2_len); n_privkey_attr++; FILL_ATTR(privkey_templ[n_privkey_attr], CKA_EXPONENT_1, rsa.exponent_1, rsa.exponent_1_len); n_privkey_attr++; FILL_ATTR(privkey_templ[n_privkey_attr], CKA_EXPONENT_2, rsa.exponent_2, rsa.exponent_2_len); n_privkey_attr++; FILL_ATTR(privkey_templ[n_privkey_attr], CKA_COEFFICIENT, rsa.coefficient, rsa.coefficient_len); n_privkey_attr++; } #if !defined(OPENSSL_NO_EC) #ifdef EVP_PKEY_ED448 else if ((pk_type == EVP_PKEY_EC) || (pk_type == EVP_PKEY_ED25519) || (pk_type == EVP_PKEY_ED448)) { #else else if ((pk_type == EVP_PKEY_EC) || (pk_type == EVP_PKEY_ED25519)) { #endif type = (pk_type == EVP_PKEY_EC) ? CKK_EC : CKK_EC_EDWARDS; 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); n_privkey_attr++; FILL_ATTR(privkey_templ[n_privkey_attr], CKA_VALUE, gost.private.value, gost.private.len); n_privkey_attr++; } else if (pk_type == NID_id_GostR3410_2001) { 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); n_privkey_attr++; FILL_ATTR(privkey_templ[n_privkey_attr], CKA_VALUE, gost.private.value, gost.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++; } #endif #endif 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 !defined(OPENSSL_NO_EC) else if (pk_type == EVP_PKEY_EC) type = CKK_EC; #ifdef EVP_PKEY_ED448 else if ((pk_type == EVP_PKEY_ED25519) || (pk_type == EVP_PKEY_ED448)) #else else if (pk_type == EVP_PKEY_ED25519) #endif type = CKK_EC_EDWARDS; else if (pk_type == NID_id_GostR3410_2001) type = CKK_GOSTR3410; #endif else util_fatal("Unsupported public key type: 0x%X", pk_type); #endif n_pubkey_attr = 0; FILL_ATTR(pubkey_templ[n_pubkey_attr], CKA_CLASS, &clazz, sizeof(clazz)); n_pubkey_attr++; FILL_ATTR(pubkey_templ[n_pubkey_attr], CKA_TOKEN, &_true, sizeof(_true)); n_pubkey_attr++; if (opt_is_private != 0) { FILL_ATTR(pubkey_templ[n_pubkey_attr], CKA_PRIVATE, &_true, sizeof(_true)); n_pubkey_attr++; } else { FILL_ATTR(pubkey_templ[n_pubkey_attr], CKA_PRIVATE, &_false, sizeof(_false)); n_pubkey_attr++; } if (opt_object_label != NULL) { FILL_ATTR(pubkey_templ[n_pubkey_attr], CKA_LABEL, opt_object_label, strlen(opt_object_label)); n_pubkey_attr++; } if (opt_object_id_len != 0) { FILL_ATTR(pubkey_templ[n_pubkey_attr], CKA_ID, opt_object_id, opt_object_id_len); n_pubkey_attr++; } if (opt_key_usage_sign != 0) { FILL_ATTR(pubkey_templ[n_pubkey_attr], CKA_VERIFY, &_true, sizeof(_true)); n_pubkey_attr++; } if (opt_key_usage_decrypt != 0) { FILL_ATTR(pubkey_templ[n_pubkey_attr], CKA_ENCRYPT, &_true, sizeof(_true)); n_pubkey_attr++; } if (opt_key_usage_derive != 0) { FILL_ATTR(pubkey_templ[n_pubkey_attr], CKA_DERIVE, &_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; 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, rsa.modulus, rsa.modulus_len); n_pubkey_attr++; FILL_ATTR(pubkey_templ[n_pubkey_attr], CKA_PUBLIC_EXPONENT, rsa.public_exponent, rsa.public_exponent_len); n_pubkey_attr++; } #if !defined(OPENSSL_NO_EC) #ifdef EVP_PKEY_ED448 else if ((pk_type == EVP_PKEY_EC) || (pk_type == EVP_PKEY_ED25519) || (pk_type == EVP_PKEY_ED448)) { #else else if ((pk_type == EVP_PKEY_EC) || (pk_type == EVP_PKEY_ED25519)) { #endif type = (pk_type == EVP_PKEY_EC) ? CKK_EC : CKK_EC_EDWARDS; 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); n_pubkey_attr++; FILL_ATTR(pubkey_templ[n_pubkey_attr], CKA_EC_POINT, gost.public.value, gost.public.len); n_pubkey_attr++; } 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); n_pubkey_attr++; FILL_ATTR(pubkey_templ[n_pubkey_attr], CKA_VALUE, gost.public.value, gost.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++; } #endif #endif 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 util_fatal("Unknown key type: 0x%lX", 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++; } 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); n_data_attr = 3; if (opt_is_private != 0) { FILL_ATTR(data_templ[n_data_attr], CKA_PRIVATE, &_true, sizeof(_true)); n_data_attr++; } else { FILL_ATTR(data_templ[n_data_attr], CKA_PRIVATE, &_false, sizeof(_false)); n_data_attr++; } if (opt_application_label != NULL) { FILL_ATTR(data_templ[n_data_attr], CKA_APPLICATION, opt_application_label, strlen(opt_application_label)); n_data_attr++; } if (opt_application_id != NULL) { size_t len; if (sc_format_oid(&oid, opt_application_id)) util_fatal("Invalid OID \"%s\"", opt_application_id); if (sc_asn1_encode_object_id(&oid_buf, &len, &oid)) util_fatal("Cannot encode OID \"%s\"", opt_application_id); FILL_ATTR(data_templ[n_data_attr], CKA_OBJECT_ID, oid_buf, len); n_data_attr++; } if (opt_object_label != NULL) { FILL_ATTR(data_templ[n_data_attr], CKA_LABEL, opt_object_label, strlen(opt_object_label)); n_data_attr++; } 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); if (rv != CKR_OK) p11_fatal("C_CreateObject", rv); printf("Created Data Object:\n"); show_dobj(session, data_obj); } if (n_cert_attr) { rv = p11->C_CreateObject(session, cert_templ, n_cert_attr, &cert_obj); if (rv != CKR_OK) p11_fatal("C_CreateObject", rv); printf("Created certificate:\n"); show_object(session, cert_obj); } if (n_pubkey_attr) { rv = p11->C_CreateObject(session, pubkey_templ, n_pubkey_attr, &pubkey_obj); if (rv != CKR_OK) p11_fatal("C_CreateObject", rv); printf("Created public key:\n"); show_object(session, pubkey_obj); } if (n_privkey_attr) { rv = p11->C_CreateObject(session, privkey_templ, n_privkey_attr, &privkey_obj); if (rv != CKR_OK) p11_fatal("C_CreateObject", rv); printf("Created private key:\n"); 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 gost_info_free(gost); EVP_PKEY_free(evp_key); #endif /* ENABLE_OPENSSL */ free(contents); free(certdata); if (oid_buf) free(oid_buf); return 1; } static void set_id_attr(CK_SESSION_HANDLE session) { CK_OBJECT_HANDLE obj; CK_ATTRIBUTE templ[] = {{CKA_ID, new_object_id, new_object_id_len}}; CK_RV rv; if (!find_object_id_or_label(session, opt_object_class, &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; } rv = p11->C_SetAttributeValue(session, obj, templ, 1); if (rv != CKR_OK) p11_fatal("C_SetAttributeValue", rv); printf("Result:"); show_object(session, obj); } static int find_slot_by_description(const char *label, CK_SLOT_ID_PTR result) { CK_SLOT_INFO info; CK_ULONG n, len; CK_RV rv; if (!p11_num_slots) return 0; len = strlen(label); for (n = 0; n < p11_num_slots; n++) { const char *slot_label; rv = p11->C_GetSlotInfo(p11_slots[n], &info); if (rv != CKR_OK) continue; slot_label = p11_utf8_to_local(info.slotDescription, sizeof(info.slotDescription)); if (!strncmp(label, slot_label, len)) { *result = p11_slots[n]; return 1; } } return 0; } static int find_slot_by_token_label(const char *label, CK_SLOT_ID_PTR result) { CK_TOKEN_INFO info; CK_ULONG n, len; CK_RV rv; if (!p11_num_slots) return 0; len = strlen(label); for (n = 0; n < p11_num_slots; n++) { const char *token_label; rv = p11->C_GetTokenInfo(p11_slots[n], &info); if (rv != CKR_OK) continue; token_label = p11_utf8_to_local(info.label, sizeof(info.label)); if (!strncmp(label, token_label, len)) { *result = p11_slots[n]; return 1; } } return 0; } static int find_object_id_or_label(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) { 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 && 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) p11_fatal("C_FindObjectsInit", rv); for (i = 0; i < obj_index; i++) { rv = p11->C_FindObjects(sess, ret, 1, &count); if (rv != CKR_OK) p11_fatal("C_FindObjects", rv); if (count == 0) goto done; } rv = p11->C_FindObjects(sess, ret, 1, &count); if (rv != CKR_OK) p11_fatal("C_FindObjects", rv); done: if (count == 0) *ret = CK_INVALID_HANDLE; p11->C_FindObjectsFinal(sess); 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, int obj_index) { return find_object_id_or_label(sess, cls, ret, id, id_len, NULL, 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, 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, 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, CK_OBJECT_HANDLE *out, CK_ATTRIBUTE *attrs, CK_ULONG attrsLen, CK_ULONG obj_index) { CK_ULONG count, ii; CK_OBJECT_HANDLE ret; CK_RV rv; if (!out || !attrs || !attrsLen) return CKR_ARGUMENTS_BAD; else *out = CK_INVALID_HANDLE; rv = p11->C_FindObjectsInit(session, attrs, attrsLen); if (rv != CKR_OK) return rv; for (ii = 0; ii < obj_index; ii++) { rv = p11->C_FindObjects(session, &ret, 1, &count); if (rv != CKR_OK) return rv; else if (!count) goto done; } rv = p11->C_FindObjects(session, &ret, 1, &count); if (rv != CKR_OK) return rv; else if (count) *out = ret; done: p11->C_FindObjectsFinal(session); return CKR_OK; } 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) { CK_MECHANISM_TYPE *mechs = NULL; CK_ULONG count = 0; count = get_mechanisms(slot, &mechs, flags); if (count) { if (list && list_len) { size_t ii = list_len, jj; for (jj=0; jjC_FindObjectsInit(sess, attrs, nn_attrs); if (rv != CKR_OK) p11_fatal("C_FindObjectsInit", rv); while (1) { rv = p11->C_FindObjects(sess, &object, 1, &count); if (rv != CKR_OK) p11_fatal("C_FindObjects", rv); if (count == 0) break; show_object(sess, object); } p11->C_FindObjectsFinal(sess); } static void show_object(CK_SESSION_HANDLE sess, CK_OBJECT_HANDLE obj) { CK_OBJECT_CLASS cls = getCLASS(sess, obj); switch (cls) { case CKO_PUBLIC_KEY: case CKO_PRIVATE_KEY: case CKO_SECRET_KEY: show_key(sess, obj); break; case CKO_CERTIFICATE: show_cert(sess, obj); break; 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, (unsigned int) cls); } } static CK_OBJECT_HANDLE derive_ec_key(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE key, CK_MECHANISM_TYPE mech_mech) { #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_OBJECT_HANDLE newkey = 0; 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)} }; int n_attrs = 9; CK_ECDH1_DERIVE_PARAMS ecdh_parms; CK_RV rv; BIO *bio_in = NULL; unsigned char *buf = NULL; size_t buf_size = 0; CK_ULONG key_len = 0; ASN1_OCTET_STRING *octet = NULL; unsigned char * der = NULL; unsigned char * derp = NULL; size_t der_size = 0; EVP_PKEY *pkey = NULL; #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)); memset(&mech, 0, sizeof(mech)); mech.mechanism = mech_mech; /* Use OpenSSL to read the other public key, and get the raw version */ bio_in = BIO_new(BIO_s_file()); if (BIO_read_filename(bio_in, opt_input) <= 0) util_fatal("Cannot open %s: %m", 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 EC key from %s", opt_input); #if OPENSSL_VERSION_NUMBER < 0x30000000L eckey = EVP_PKEY_get0_EC_KEY(pkey); ecpoint = EC_KEY_get0_public_key(eckey); ecgroup = EC_KEY_get0_group(eckey); if (!ecpoint || !ecgroup) util_fatal("Failed to parse other 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 other EC key from %s", opt_input); #endif /* 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 (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++; } #if OPENSSL_VERSION_NUMBER < 0x30000000L 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); #else EC_GROUP_free(ecgroup); EVP_PKEY_get_octet_string_param(pkey, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0, &buf_size); if ((buf = (unsigned char *)malloc(buf_size)) == NULL) util_fatal("malloc() failure\n"); 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 (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, (int)buf_size); der_size = i2d_ASN1_OCTET_STRING(octet, NULL); derp = der = (unsigned char *) malloc(der_size); if (der == NULL) util_fatal("malloc() failure\n"); der_size = i2d_ASN1_OCTET_STRING(octet, &derp); } BIO_free(bio_in); EVP_PKEY_free(pkey); memset(&ecdh_parms, 0, sizeof(ecdh_parms)); ecdh_parms.kdf = CKD_NULL; ecdh_parms.ulSharedDataLen = 0; ecdh_parms.pSharedData = NULL; if (opt_derive_pass_der) { ecdh_parms.ulPublicDataLen = der_size; ecdh_parms.pPublicData = der; } else { ecdh_parms.ulPublicDataLen = buf_size; ecdh_parms.pPublicData = buf; } mech.pParameter = &ecdh_parms; mech.ulParameterLen = sizeof(ecdh_parms); rv = p11->C_DeriveKey(session, &mech, key, newkey_template, n_attrs, &newkey); if (rv != CKR_OK) p11_fatal("C_DeriveKey", rv); 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 */ } 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 fd; ssize_t sz; if (!opt_mechanism_used) 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); break; case CKM_HKDF_DERIVE: derived_key = derive_hkdf(session, key); break; default: util_fatal("mechanism not supported for derive"); break; } value = getVALUE(session, derived_key, &value_len); if (value && value_len > 0) { fd = STDOUT_FILENO; if (opt_output) { 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); } sz = write(fd, value, value_len); free(value); if (sz < 0) util_fatal("Failed to write to %s: %m", opt_output); if (opt_output) close(fd); } } 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, 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; switch(getCLASS(sess, obj)) { case CKO_PRIVATE_KEY: printf("Private Key Object"); pub = 0; break; case CKO_PUBLIC_KEY: printf("Public Key Object"); pub = 1; break; case CKO_SECRET_KEY: printf("Secret Key Object"); sec = 1; break; default: return; } switch (key_type) { case CKK_RSA: if (sec) { /* uninitialized secret key (type 0) */ printf("\n"); } else { if (pub) printf("; RSA %lu bits\n", (unsigned long) getMODULUS_BITS(sess, obj)); else printf("; RSA \n"); } break; case CKK_GOSTR3410: case CKK_GOSTR3410_512: oid = getGOSTR3411_PARAMS(sess, obj, &size); if (oid) { 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"); } oid = getGOSTR3410_PARAMS(sess, obj, &size); if (oid) { unsigned int n; printf(" PARAMS OID: "); for (n = 0; n < size; n++) printf("%02x", oid[n]); printf("\n"); 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"); free(value); } } break; case CKK_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 long ksize; unsigned int n; bytes = getEC_POINT(sess, obj, &size); if (key_type == CKK_EC) { /* * (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" * Do simple size calculation based on DER encoding */ if ((size - 2) <= 127) ksize = (size - 3) * 4; else if ((size - 3) <= 255) ksize = (size - 4) * 4; else ksize = (size - 5) * 4; } else { /* This should be 255 for ed25519 and 448 for ed448 curves so roughly */ ksize = size * 8; } printf(" EC_POINT %lu 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); } bytes = NULL; bytes = getEC_PARAMS(sess, obj, &size); if (bytes){ if ((CK_LONG)size > 0) { struct sc_object_id oid; printf(" EC_PARAMS: "); for (n = 0; n < size; n++) printf("%02x", bytes[n]); if (size > 2 && bytes[0] == 0x06) { // OID sc_init_oid(&oid); if (sc_asn1_decode_object_id(bytes + 2, size - 2, &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] >= 0); n++) printf(".%i", oid.value[n]); printf(")"); } } else if (size > 2 && bytes[0] == 0x13) { // Printable string printf(" (PrintableString %.*s)", bytes[1], bytes+2); } printf("\n"); } free(bytes); } } else { printf("\n"); } break; case CKK_GENERIC_SECRET: case CKK_AES: case CKK_DES: case CKK_DES3: case CKK_HKDF: 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 printf("; Generic secret"); size = getVALUE_LEN(sess, obj); if (size) printf(" length %li", size); size = 0; 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"); free(value); } break; default: printf("; unknown key algorithm %lu\n", (unsigned long) key_type); break; } if ((label = getLABEL(sess, obj, NULL)) != NULL) { printf(" label: %s\n", label); } if ((id = getID(sess, obj, &idsize)) != NULL && idsize) { unsigned int n; printf(" ID: "); for (n = 0; n < idsize; n++) printf("%02x", id[n]); printf("\n"); } printf(" Usage: "); sepa = ""; if ((pub || sec) && getENCRYPT(sess, obj)) { printf("%sencrypt", sepa); sepa = ", "; } if ((!pub || sec) && getDECRYPT(sess, obj)) { printf("%sdecrypt", sepa); sepa = ", "; } 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)) { printf("%snon-repudiation", sepa); sepa = ", "; } suppress_warn = 0; if (pub && getVERIFY(sess, obj)) { printf("%sverify", sepa); sepa = ", "; } if (pub && getVERIFY_RECOVER(sess, obj)) { printf("%sverifyRecover", sepa); sepa = ", "; } if ((pub || sec) && getWRAP(sess, obj)) { printf("%swrap", sepa); sepa = ", "; } if ((!pub || sec) && getUNWRAP(sess, obj)) { printf("%sunwrap", sepa); sepa = ", "; } if (getDERIVE(sess, obj)) { printf("%sderive", 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) { 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"); } } 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)); 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; } 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) { case CKC_X_509: printf("X.509 cert\n"); break; case CKC_X_509_ATTR_CERT: printf("X.509 attribute cert\n"); break; case CKC_VENDOR_DEFINED: printf("vendor defined\n"); break; default: printf("unknown cert type\n"); break; } if ((label = getLABEL(sess, obj, NULL)) != NULL) { printf(" label: %s\n", label); } #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(bio, name, 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) { unsigned int n; printf(" ID: "); for (n = 0; n < size; n++) printf("%02x", id[n]); printf("\n"); } 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)); 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); } else { printf("\n"); } printf(" application: "); if ((application = getAPPLICATION(sess, obj, NULL)) != NULL) { printf("'%s'\n", application); free(application); } else { printf("\n"); } printf(" app_id: "); oid_buf = getOBJECT_ID(sess, obj, &size); if (oid_buf != NULL && size) { unsigned int n; struct sc_object_id oid; sc_init_oid(&oid); sc_asn1_decode_object_id(oid_buf, size, &oid); printf("%i", oid.value[0]); if (oid.value[0] >= 0) for (n = 1; (n < SC_MAX_OBJECT_ID_OCTETS) && (oid.value[n] >= 0); n++) printf(".%i", oid.value[n]); printf("\n"); free(oid_buf); } else { printf("\n"); } printf(" flags: "); modifiable = getMODIFIABLE(sess, obj); if (modifiable) printf(" modifiable"); private = getPRIVATE(sess, obj); if (private) printf(" private"); if (!modifiable && !private) printf(""); printf("\n"); get_token_info(opt_slot, &info); printf(" uri: %s", get_uri(&info)); 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) { CK_RV rv; rv = p11->C_GetTokenInfo(slot, info); if (rv != CKR_OK) p11_fatal("C_GetTokenInfo", rv); } static CK_ULONG get_mechanisms(CK_SLOT_ID slot, CK_MECHANISM_TYPE_PTR *pList, CK_FLAGS flags) { CK_ULONG m, n, ulCount = 0; 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"); rv = p11->C_GetMechanismList(slot, *pList, &ulCount); if (rv != CKR_OK) p11_fatal("C_GetMechanismList", rv); if (flags != (CK_FLAGS)-1) { CK_MECHANISM_TYPE *mechs = *pList; CK_MECHANISM_INFO info; for (m = n = 0; n < ulCount; n++) { rv = p11->C_GetMechanismInfo(slot, mechs[n], &info); if (rv != CKR_OK) continue; if ((info.flags & flags) == flags) mechs[m++] = mechs[n]; } ulCount = m; } return ulCount; } #ifdef ENABLE_OPENSSL unsigned char *BIO_copy_data(BIO *out, long *data_lenp) { unsigned char *data, *tdata; long data_len; data_len = BIO_get_mem_data(out, &tdata); data = malloc(data_len+1); if (data) { memcpy(data, tdata, data_len); data[data_len]='\0'; // Make sure it's \0 terminated, in case used as string if (data_lenp) { *data_lenp = data_len; } } else { util_fatal("malloc failed"); } return data; } #endif /* * Read object CKA_VALUE attribute's value. */ static int read_object(CK_SESSION_HANDLE session) { CK_RV rv; CK_ATTRIBUTE attrs[20]; CK_OBJECT_CLASS clazz = opt_object_class; #ifdef ENABLE_OPENSSL CK_KEY_TYPE type = CKK_RSA; #endif CK_OBJECT_HANDLE obj = CK_INVALID_HANDLE; int nn_attrs = 0; unsigned char *value = NULL, *oid_buf = NULL; CK_ULONG len = 0; FILE *out; struct sc_object_id oid; unsigned char subject[0x400], issuer[0x400]; if (opt_object_class_str != NULL) { FILL_ATTR(attrs[nn_attrs], CKA_CLASS, &clazz, sizeof(clazz)); 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++; } if (opt_application_id != NULL) { size_t oid_buf_len; if (sc_format_oid(&oid, opt_application_id)) util_fatal("Invalid OID \"%s\"", opt_application_id); if (sc_asn1_encode_object_id(&oid_buf, &oid_buf_len, &oid)) util_fatal("Cannot encode OID \"%s\"", opt_application_id); FILL_ATTR(attrs[nn_attrs], CKA_OBJECT_ID, oid_buf, oid_buf_len); nn_attrs++; } if (opt_issuer != NULL) { size_t sz = sizeof(issuer); if (sc_hex_to_bin(opt_issuer, issuer, &sz)) util_fatal("Invalid 'issuer' hexadecimal value"); FILL_ATTR(attrs[nn_attrs], CKA_ISSUER, issuer, sz); nn_attrs++; } if (opt_subject != NULL) { size_t sz = sizeof(subject); if (sc_hex_to_bin(opt_subject, subject, &sz)) util_fatal("Invalid 'subject' hexadecimal value"); FILL_ATTR(attrs[nn_attrs], CKA_SUBJECT, subject, sz); nn_attrs++; } 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) { #ifdef ENABLE_OPENSSL 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) { 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"); 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 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 = os->data; a_len = os->length; 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 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); 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); #endif if (!i2d_PUBKEY_bio(pout, pkey)) util_fatal("cannot convert EC public key to DER"); #endif #ifdef EVP_PKEY_ED25519 } else if (type == CKK_EC_EDWARDS) { EVP_PKEY *key = NULL; CK_BYTE *params = NULL; const unsigned char *a; ASN1_OCTET_STRING *os; 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 *)curve->data, "edwards25519")) { util_fatal("Unknown curve name, expected edwards25519, got %s", curve->data); } ASN1_PRINTABLESTRING_free(curve); } else if (d2i_ASN1_OBJECT(&obj, &a, (long)len) != NULL) { int nid = OBJ_obj2nid(obj); if (nid != NID_ED25519) { util_fatal("Unknown curve OID, expected NID_ED25519 (%d), got %d", NID_ED25519, nid); } ASN1_OBJECT_free(obj); } else { util_fatal("cannot parse curve name from EC_PARAMS"); } 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) { util_fatal("cannot decode EC_POINT"); } if (os->length != 32) { util_fatal("Invalid length of EC_POINT value"); } key = EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, NULL, (const uint8_t *)os->data, os->length); ASN1_STRING_free(os); if (key == NULL) { util_fatal("out of memory"); } /* Note, that we write PEM here as there is no "native" * representation of EdDSA public keys to use */ if (!PEM_write_bio_PUBKEY(pout, key)) { util_fatal("cannot convert EdDSA public key to PEM"); } EVP_PKEY_free(key); #endif } 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 } else value = getVALUE(session, obj, &len); if (value == NULL) util_fatal("get CKA_VALUE failed"); if (opt_output) { out = fopen(opt_output, "wb"); if (out==NULL) util_fatal("cannot open '%s'", opt_output); } else out = stdout; if (fwrite(value, 1, len, out) != len) util_fatal("cannot write to '%s'", opt_output); if (opt_output) fclose(out); free(value); if (oid_buf) free(oid_buf); return 1; } /* * Delete object. */ static int delete_object(CK_SESSION_HANDLE session) { CK_RV rv; CK_ATTRIBUTE attrs[20]; CK_OBJECT_CLASS clazz = opt_object_class; CK_OBJECT_HANDLE obj = CK_INVALID_HANDLE; int nn_attrs = 0; struct sc_object_id oid; unsigned char *oid_buf = NULL; if (opt_object_class_str != NULL) { FILL_ATTR(attrs[nn_attrs], CKA_CLASS, &clazz, sizeof(clazz)); 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++; } if (opt_application_id != NULL) { size_t oid_buf_len; if (sc_format_oid(&oid, opt_application_id)) util_fatal("Invalid OID '%s'", opt_application_id); if (sc_asn1_encode_object_id(&oid_buf, &oid_buf_len, &oid)) util_fatal("Cannot encode OID \"%s\"", opt_application_id); FILL_ATTR(attrs[nn_attrs], CKA_OBJECT_ID, oid_buf, oid_buf_len); nn_attrs++; } 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"); rv = p11->C_DestroyObject(session, obj); if (rv != CKR_OK) p11_fatal("C_DestroyObject()", rv); if (oid_buf) free(oid_buf); return 1; } static CK_ULONG get_private_key_length(CK_SESSION_HANDLE sess, CK_OBJECT_HANDLE prkey) { unsigned char *id; CK_ULONG idLen; CK_OBJECT_HANDLE pubkey; id = NULL; id = getID(sess, prkey, &idLen); if (id == NULL) { fprintf(stderr, "private key has no ID, can't lookup the corresponding pubkey\n"); return 0; } if (!find_object(sess, CKO_PUBLIC_KEY, &pubkey, id, idLen, 0)) { free(id); fprintf(stderr, "couldn't find the corresponding pubkey\n"); return 0; } free(id); return getMODULUS_BITS(sess, pubkey); } static int test_digest(CK_SESSION_HANDLE session) { int errors = 0; CK_RV rv; CK_MECHANISM ck_mech = { CKM_MD5, NULL, 0 }; CK_ULONG i, j; unsigned char data[100]; unsigned char hash1[64], hash2[64]; CK_ULONG hashLen1, hashLen2; CK_MECHANISM_TYPE firstMechType; CK_SESSION_INFO sessionInfo; CK_MECHANISM_TYPE mechTypes[] = { CKM_MD5, 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 *) "\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, 32, }; rv = p11->C_GetSessionInfo(session, &sessionInfo); if (rv != CKR_OK) p11_fatal("C_OpenSession", rv); if (!find_mechanism(sessionInfo.slotID, CKF_DIGEST, NULL, 0, &firstMechType)) { fprintf(stderr, "Digests: not implemented\n"); return errors; } else { printf("Digests:\n"); } /* 1st test */ pseudo_randomize(data, sizeof(data)); ck_mech.mechanism = firstMechType; rv = p11->C_DigestInit(session, &ck_mech); if (rv != CKR_OK) p11_fatal("C_DigestInit", rv); rv = p11->C_DigestUpdate(session, data, 5); if (rv == CKR_FUNCTION_NOT_SUPPORTED) { printf(" Note: C_DigestUpdate(), DigestFinal() not supported\n"); /* finish the digest operation */ hashLen2 = sizeof(hash2); rv = p11->C_Digest(session, data, sizeof(data), hash2, &hashLen2); if (rv != CKR_OK) p11_fatal("C_Digest", rv); } else { if (rv != CKR_OK) p11_fatal("C_DigestUpdate", rv); rv = p11->C_DigestUpdate(session, data + 5, 50); if (rv != CKR_OK) p11_fatal("C_DigestUpdate", rv); rv = p11->C_DigestUpdate(session, data + 55, sizeof(data) - 55); if (rv != CKR_OK) p11_fatal("C_DigestUpdate", rv); hashLen1 = sizeof(hash1); rv = p11->C_DigestFinal(session, hash1, &hashLen1); if (rv != CKR_OK) p11_fatal("C_DigestFinal", rv); rv = p11->C_DigestInit(session, &ck_mech); if (rv != CKR_OK) p11_fatal("C_DigestInit", rv); hashLen2 = sizeof(hash2); rv = p11->C_Digest(session, data, sizeof(data), hash2, &hashLen2); if (rv != CKR_OK) p11_fatal("C_Digest", rv); if (hashLen1 != hashLen2) { errors++; printf(" ERR: digest lengths returned by C_DigestFinal() different from C_Digest()\n"); } else if (memcmp(hash1, hash2, hashLen1) != 0) { errors++; printf(" ERR: digests returned by C_DigestFinal() different from C_Digest()\n"); } else printf(" all 4 digest functions seem to work\n"); } /* 2nd test */ /* input = "01234567890123456...456789" */ for (i = 0; i < 10; i++) for (j = 0; j < 10; j++) data[10 * i + j] = (unsigned char) (0x30 + j); #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); if (rv == CKR_MECHANISM_INVALID) continue; /* mechanism not implemented, don't test */ if (rv != CKR_OK) p11_fatal("C_DigestInit", rv); printf(" %s: ", p11_mechanism_to_name(mechTypes[i])); hashLen1 = sizeof(hash1); rv = p11->C_Digest(session, data, sizeof(data), hash1, &hashLen1); if (rv != CKR_OK) p11_fatal("C_Digest", rv); if (hashLen1 != digestLens[i]) { errors++; printf("ERR: wrong digest length: %ld instead of %ld\n", hashLen1, digestLens[i]); } else if (memcmp(hash1, digests[i], hashLen1) != 0) { errors++; printf("ERR: wrong digest value\n"); } else printf("OK\n"); } /* 3rd test */ ck_mech.mechanism = firstMechType; rv = p11->C_DigestInit(session, &ck_mech); if (rv != CKR_OK) p11_fatal("C_DigestInit", rv); hashLen2 = 1; /* too short */ rv = p11->C_Digest(session, data, sizeof(data), hash2, &hashLen2); if (rv != CKR_BUFFER_TOO_SMALL) { errors++; printf(" ERR: C_Digest() didn't return CKR_BUFFER_TOO_SMALL but %s (0x%0x)\n", CKR2Str(rv), (int) rv); } /* output buffer = NULL */ rv = p11->C_Digest(session, data, sizeof(data), NULL, &hashLen2); if (rv != CKR_OK) { errors++; printf(" ERR: C_Digest() didn't return CKR_OK for a NULL output buffer, but %s (0x%0x)\n", CKR2Str(rv), (int) rv); } rv = p11->C_Digest(session, data, sizeof(data), hash2, &hashLen2); if (rv == CKR_OPERATION_NOT_INITIALIZED) { printf(" ERR: digest operation ended prematurely\n"); errors++; } else if (rv != CKR_OK) p11_fatal("C_Sign", rv); 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) { CK_BYTE *id, *mod, *exp; CK_ULONG idLen = 0, modLen = 0, expLen = 0; CK_OBJECT_HANDLE pubkeyObject; unsigned char *pubkey; const unsigned char *pubkey_c; CK_ULONG pubkeyLen; 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); if (id == NULL) { fprintf(stderr, "private key has no ID, can't lookup the corresponding pubkey for verification\n"); return NULL; } if (!find_object(session, CKO_PUBLIC_KEY, &pubkeyObject, id, idLen, 0)) { free(id); fprintf(stderr, "couldn't find the corresponding pubkey for validation\n"); return NULL; } free(id); switch(getKEY_TYPE(session, pubkeyObject)) { case CKK_RSA:; #if OPENSSL_VERSION_NUMBER < 0x30000000L rsa = RSA_new(); 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, (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); #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: case CKK_GOSTR3410: break; default: fprintf(stderr, "public key of unsupported type\n"); return NULL; } pubkey = getVALUE(session, pubkeyObject, &pubkeyLen); if (pubkey == NULL) { fprintf(stderr, "couldn't get the pubkey VALUE attribute, no validation done\n"); return NULL; } pubkey_c = pubkey; pkey = d2i_PublicKey(EVP_PKEY_RSA, NULL, &pubkey_c, pubkeyLen); free(pubkey); if (pkey == NULL) { fprintf(stderr, "couldn't parse pubkey, no verification done\n"); return NULL; } return pkey; } #endif static int sign_verify_openssl(CK_SESSION_HANDLE session, CK_MECHANISM *ck_mech, CK_OBJECT_HANDLE privKeyObject, unsigned char *data, CK_ULONG dataLen, unsigned char *verifyData, CK_ULONG verifyDataLen, CK_ULONG modLenBytes, int evp_md_index) { int errors = 0; CK_RV rv; unsigned char sig1[1024]; CK_ULONG sigLen1; #ifdef ENABLE_OPENSSL int err; EVP_PKEY *pkey; EVP_MD_CTX *md_ctx; const EVP_MD *evp_mds[] = { EVP_sha1(), EVP_sha1(), EVP_sha1(), EVP_md5(), #ifndef OPENSSL_NO_RIPEMD EVP_ripemd160(), #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 */ if (rv == CKR_MECHANISM_INVALID) return errors; if (rv != CKR_OK) p11_fatal("C_SignInit", rv); 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)); sigLen1 = sizeof(sig1); rv = p11->C_Sign(session, data, dataLen, sig1, &sigLen1); if (rv != CKR_OK) p11_fatal("C_Sign", rv); if (sigLen1 != modLenBytes) { errors++; printf(" ERR: wrong signature length: %u instead of %u\n", (unsigned int) sigLen1, (unsigned int) modLenBytes); } #ifndef ENABLE_OPENSSL fprintf(stderr, "unable to verify signature (compile with ENABLE_OPENSSL)\n"); #else if (!(pkey = get_public_key(session, privKeyObject))) return errors; md_ctx = EVP_MD_CTX_create(); if (!md_ctx) err = -1; else { 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); } if (err == 0) { printf("ERR: verification failed\n"); errors++; } else if (err != 1) { printf("openssl error during verification: 0x%0x (%d)\n", err, err); } else printf("OK\n"); /* free(cert); */ #endif return errors; } /* * Test signature functions */ static int test_signature(CK_SESSION_HANDLE sess) { int errors = 0; CK_RV rv; CK_OBJECT_HANDLE pubKeyObject, privKeyObject; CK_MECHANISM ck_mech = { CKM_MD5, NULL, 0 }; CK_MECHANISM_TYPE firstMechType; CK_SESSION_INFO sessionInfo; int i, j; unsigned char data[512]; /* FIXME: Will not work for keys above 4096 bits */ CK_ULONG modLenBytes = 0; CK_ULONG dataLen; unsigned char sig1[1024], sig2[1024]; CK_ULONG sigLen1, sigLen2; unsigned char verifyData[100]; char *label; CK_MECHANISM_TYPE mechTypes[] = { CKM_RSA_X_509, CKM_RSA_PKCS, CKM_SHA1_RSA_PKCS, CKM_MD5_RSA_PKCS, #ifndef OPENSSL_NO_RIPEMD CKM_RIPEMD160_RSA_PKCS, #endif CKM_SHA256_RSA_PKCS, 0xffffff }; size_t mechTypes_num = sizeof(mechTypes)/sizeof(CK_MECHANISM_TYPE); unsigned char *datas[] = { /* PCKS1_wrap(SHA1_encode(SHA-1(verifyData))), * is done further on */ NULL, /* SHA1_encode(SHA-1(verifyData)) */ (unsigned char *) "\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14\x29\xb0\xe7\x87\x82\x71\x64\x5f\xff\xb7\xee\xc7\xdb\x4a\x74\x73\xa1\xc0\x0b\xc1", verifyData, verifyData, verifyData, verifyData, }; CK_ULONG dataLens[] = { 0, /* should be modulus length, is done further on */ 35, sizeof(verifyData), sizeof(verifyData), sizeof(verifyData), sizeof(verifyData), }; rv = p11->C_GetSessionInfo(sess, &sessionInfo); if (rv != CKR_OK) p11_fatal("C_OpenSession", rv); if (!(sessionInfo.state & CKS_RW_USER_FUNCTIONS)) { printf("Signature: not a R/W session, skipping signature tests\n"); return errors; } 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 %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 (!getSIGN(sess, privKeyObject)) { printf(" -- can't be used for signature, skipping\n"); continue; } modLenBytes = (get_private_key_length(sess, privKeyObject) + 7) / 8; if(!modLenBytes) { printf(" -- can't be used for signature, skipping: can't obtain modulus\n"); continue; } printf("\n"); break; } if (privKeyObject == CK_INVALID_HANDLE) { fprintf(stderr, "Signatures: no private key found in this slot\n"); return 0; } /* 1st test */ /* assume --login has already authenticated the key */ switch (firstMechType) { case CKM_RSA_PKCS: dataLen = 35; memcpy(data, datas[1], dataLen); 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; } if (firstMechType == CKM_RSA_X_509) { /* make sure our data is smaller than the modulus - 11 */ memset(data, 0, 11); /* in effect is zero padding */ } ck_mech.mechanism = firstMechType; rv = p11->C_SignInit(sess, &ck_mech, privKeyObject); /* mechanism not implemented, don't test */ if (rv == CKR_MECHANISM_INVALID) return errors; 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); rv = p11->C_SignUpdate(sess, data, 5); if (rv == CKR_FUNCTION_NOT_SUPPORTED) { p11_warn("C_SignUpdate", rv); } else if (rv != CKR_OK) { p11_perror("C_SignUpdate", rv); errors++; } else { if (rv != CKR_OK) p11_fatal("C_SignUpdate", rv); rv = p11->C_SignUpdate(sess, data + 5, 10); if (rv != CKR_OK) p11_fatal("C_SignUpdate", rv); rv = p11->C_SignUpdate(sess, data + 15, dataLen - 15); if (rv != CKR_OK) p11_fatal("C_SignUpdate", rv); sigLen1 = sizeof(sig1); rv = p11->C_SignFinal(sess, sig1, &sigLen1); if (rv != CKR_OK) p11_fatal("C_SignFinal", rv); 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 = sizeof(sig2); rv = p11->C_Sign(sess, data, dataLen, sig2, &sigLen2); if (rv != CKR_OK) p11_fatal("C_Sign", rv); if (sigLen1 != sigLen2) { errors++; printf(" ERR: signature lengths returned by C_SignFinal() different from C_Sign()\n"); } else if (memcmp(sig1, sig2, sigLen1) != 0) { errors++; printf(" ERR: signatures returned by C_SignFinal() different from C_Sign()\n"); } else printf(" all 4 signature functions seem to work\n"); } /* 2nd test */ ck_mech.mechanism = firstMechType; 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); if (rv != CKR_BUFFER_TOO_SMALL) { errors++; printf(" ERR: C_Sign() didn't return CKR_BUFFER_TOO_SMALL but %s (0x%0x)\n", CKR2Str(rv), (int) rv); } /* output buf = NULL */ rv = p11->C_Sign(sess, data, dataLen, NULL, &sigLen2); if (rv != CKR_OK) { errors++; printf(" ERR: C_Sign() didn't return CKR_OK for a NULL output buf, but %s (0x%0x)\n", CKR2Str(rv), (int) rv); } 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); if (rv == CKR_OPERATION_NOT_INITIALIZED) { printf(" ERR: signature operation ended prematurely\n"); errors++; } else if (rv != CKR_OK) p11_fatal("C_Sign", rv); /* 3rd test */ /* input = "01234567890123456...456789" */ for (i = 0; i < 10; i++) for (j = 0; j < 10; j++) verifyData[10 * i + j] = (unsigned char) (0x30 + j); /* Fill in data[0] and dataLens[0] */ dataLen = modLenBytes; data[0] = 0x00; data[1] = 0x01; memset(data + 2, 0xFF, dataLen - 3 - dataLens[1]); if (dataLen >= 36) data[dataLen - 36] = 0x00; memcpy(data + (dataLen - dataLens[1]), datas[1], dataLens[1]); datas[0] = data; dataLens[0] = dataLen; printf(" testing signature mechanisms:\n"); for (i = 0; mechTypes[i] != 0xffffff; i++) { ck_mech.mechanism = mechTypes[i]; errors += sign_verify_openssl(sess, &ck_mech, privKeyObject, datas[i], dataLens[i], verifyData, sizeof(verifyData), modLenBytes, i); } /* 4th test: the other signature keys */ for (i = 0; mechTypes[i] != 0xffffff; i++) if (mechTypes[i] == firstMechType) break; ck_mech.mechanism = mechTypes[i]; j = 1; /* j-th signature key */ while (find_object(sess, CKO_PRIVATE_KEY, &privKeyObject, NULL, 0, j++) != 0) { unsigned char *id; CK_ULONG idLen; CK_ULONG modLenBits; 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"); continue; } if (!getSIGN(sess, privKeyObject)) { printf(" -- can't be used to sign/verify, skipping\n"); continue; } if ((id = getID(sess, privKeyObject, &idLen)) != NULL) { int r; r = find_object(sess, CKO_PUBLIC_KEY, &pubKeyObject, id, idLen, 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; } modLenBits = get_private_key_length(sess, privKeyObject); modLenBytes = (modLenBits + 7) / 8; 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; data[0] = 0x00; data[1] = 0x01; memset(data + 2, 0xFF, dataLen - 3 - dataLens[1]); data[dataLen - 36] = 0x00; memcpy(data + (dataLen - dataLens[1]), datas[1], dataLens[1]); datas[0] = data; dataLens[0] = dataLen; errors += sign_verify_openssl(sess, &ck_mech, privKeyObject, datas[i], dataLens[i], verifyData, sizeof(verifyData), modLenBytes, i); } return errors; } static int sign_verify(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE priv_key, int key_len, CK_OBJECT_HANDLE pub_key, int one_test) { CK_RV rv; CK_MECHANISM_TYPE mech_types[] = { CKM_RSA_X_509, CKM_RSA_PKCS, CKM_SHA1_RSA_PKCS, CKM_MD5_RSA_PKCS, CKM_RIPEMD160_RSA_PKCS, 0xffffff }; CK_MECHANISM_TYPE *mech_type; unsigned char buf[512] = {0}; unsigned char *datas[] = { buf, (unsigned char *) "\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14\x29\xb0\xe7\x87\x82\x71\x64\x5f\xff\xb7\xee\xc7\xdb\x4a\x74\x73\xa1\xc0\x0b\xc1", buf, buf, buf }; int data_lens[] = { key_len, 35, 234, 345, 456 }; unsigned char signat[512]; CK_ULONG signat_len; int j, errors = 0; memcpy(buf, "\x00\x01\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00", 11); for (j = 0, mech_type = mech_types; *mech_type != 0xffffff; mech_type++, j++) { CK_MECHANISM mech = {*mech_type, NULL, 0}; rv = p11->C_SignInit(session, &mech, priv_key); if (rv == CKR_MECHANISM_INVALID) continue; if (rv != CKR_OK) { printf(" ERR: C_SignInit() returned %s (0x%0x)\n", CKR2Str(rv), (int) rv); return ++errors; } 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)); signat_len = sizeof(signat); rv = p11->C_Sign(session, datas[j], data_lens[j], signat, &signat_len); if (rv != CKR_OK) { printf(" ERR: C_Sign() returned %s (0x%0x)\n", CKR2Str(rv), (int) rv); return ++errors; } rv = p11->C_VerifyInit(session, &mech, pub_key); if (rv != CKR_OK) { printf(" ERR: C_VerifyInit() returned %s (0x%0x)\n", CKR2Str(rv), (int) rv); return ++errors; } rv = p11->C_Verify(session, datas[j], data_lens[j], signat, signat_len); if (rv == CKR_SIGNATURE_INVALID) { printf(" ERR: verification failed"); errors++; } if (rv != CKR_OK) { printf(" ERR: C_Verify() returned %s (0x%0x)\n", CKR2Str(rv), (int) rv); return ++errors; } else printf("OK\n"); if (one_test) return errors; } return errors; } static int test_verify(CK_SESSION_HANDLE sess) { 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; CK_RV rv; rv = p11->C_GetSessionInfo(sess, &sessionInfo); if (rv != CKR_OK) p11_fatal("C_OpenSession", rv); if (!(sessionInfo.state & CKS_RW_USER_FUNCTIONS)) { printf("Verify: not a R/W session, skipping verify tests\n"); return errors; } if (!find_mechanism(sessionInfo.slotID, CKF_VERIFY, NULL, 0, &first_mech_type)) { printf("Verify: not implemented\n"); return errors; } printf("Verify (currently only for RSA)\n"); for (i = 0; find_object(sess, CKO_PRIVATE_KEY, &priv_key, NULL, 0, i); i++) { char *label; unsigned char *id; CK_ULONG id_len; printf(" testing key %d", i); if ((label = getLABEL(sess, priv_key, NULL)) != NULL) { printf(" (%s)", label); free(label); } if (i != 0) printf(" with 1 mechanism"); if (getKEY_TYPE(sess, priv_key) != CKK_RSA) { printf(" -- non-RSA, skipping\n"); continue; } if (!getSIGN(sess, priv_key)) { printf(" -- can't be used to sign/verify, skipping\n"); continue; } if ((id = getID(sess, priv_key, &id_len)) != NULL) { int r; r = find_object(sess, CKO_PUBLIC_KEY, &pub_key, id, id_len, 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; } key_len = (get_private_key_length(sess, priv_key) + 7) / 8; 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, (int)key_len, pub_key, i != 0); } if (i == 0) printf(" No private key found for testing\n"); return errors; } #if OPENSC_VERSION_MAJOR == 0 && OPENSC_VERSION_MINOR <= 26 #else #ifdef ENABLE_OPENSSL static int wrap_unwrap(CK_SESSION_HANDLE session, const EVP_CIPHER *algo, CK_OBJECT_HANDLE privKeyObject) { CK_OBJECT_HANDLE cipherKeyObject; CK_RV rv; EVP_PKEY *pkey; EVP_CIPHER_CTX * seal_ctx; unsigned char keybuf[512], *key = keybuf; int key_len; unsigned char iv[32], ciphered[1024], cleartext[1024]; int ciphered_len, cleartext_len, len; CK_MECHANISM mech; CK_ULONG key_type = CKM_DES_CBC; CK_ULONG key_len_ul; CK_ATTRIBUTE key_template = { CKA_KEY_TYPE, &key_type, sizeof(key_type) }; pkey = get_public_key(session, privKeyObject); if (pkey == NULL) return 0; printf(" %s: ", OBJ_nid2sn(EVP_CIPHER_nid(algo))); seal_ctx = EVP_CIPHER_CTX_new(); if (seal_ctx == NULL) { printf("Internal error.\n"); return 1; } if (!EVP_SealInit(seal_ctx, algo, &key, &key_len, iv, &pkey, 1)) { fprintf(stderr, "Internal error.\n"); return 1; } /* Encrypt something */ len = sizeof(ciphered); if (!EVP_SealUpdate(seal_ctx, ciphered, &len, (const unsigned char *) "hello world", 11)) { printf("Internal error.\n"); return 1; } ciphered_len = len; len = sizeof(ciphered) - ciphered_len; if (!EVP_SealFinal(seal_ctx, ciphered + ciphered_len, &len)) { printf("Internal error.\n"); return 1; } ciphered_len += len; EVP_PKEY_free(pkey); mech.mechanism = CKM_RSA_PKCS; rv = p11->C_UnwrapKey(session, &mech, privKeyObject, key, key_len, &key_template, 1, &cipherKeyObject); /* mechanism not implemented, don't test */ if (rv == CKR_MECHANISM_INVALID) { printf("Wrap mechanism not supported, skipped\n"); return 0; } if (rv != CKR_OK) { p11_perror("C_UnwrapKey", rv); return 1; } /* Try to decrypt */ key = getVALUE(session, cipherKeyObject, &key_len_ul); key_len = (int)key_len_ul; if (key == NULL) { fprintf(stderr, "Could not get unwrapped key\n"); return 1; } if (key_len != EVP_CIPHER_key_length(algo)) { fprintf(stderr, "Key length mismatch (%d != %d)\n", key_len, EVP_CIPHER_key_length(algo)); return 1; } if (!EVP_DecryptInit(seal_ctx, algo, key, iv)) { printf("Internal error.\n"); return 1; } len = sizeof(cleartext); if (!EVP_DecryptUpdate(seal_ctx, cleartext, &len, ciphered, ciphered_len)) { printf("Internal error.\n"); return 1; } cleartext_len = len; len = sizeof(cleartext) - len; if (!EVP_DecryptFinal(seal_ctx, cleartext + cleartext_len, &len)) { printf("Internal error.\n"); return 1; } cleartext_len += len; if (cleartext_len != 11 || memcmp(cleartext, "hello world", 11)) { printf("resulting cleartext doesn't match input\n"); return 1; } if (seal_ctx) EVP_CIPHER_CTX_free(seal_ctx); printf("OK\n"); return 0; } #endif #endif /* * Test unwrap functions */ static int test_unwrap(CK_SESSION_HANDLE sess) { #if OPENSC_VERSION_MAJOR == 0 && OPENSC_VERSION_MINOR <= 26 /* 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; int j; char *label; rv = p11->C_GetSessionInfo(sess, &sessionInfo); if (rv != CKR_OK) p11_fatal("C_OpenSession", rv); if (!(sessionInfo.state & CKS_RW_USER_FUNCTIONS)) { printf("Key unwrap: not a R/W session, skipping key unwrap tests\n"); return errors; } 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 %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 (!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 = (get_private_key_length(session, privKeyObject) + 7) / 8; 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 (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; } } #else if (oaep_params.hashAlg != CKM_SHA_1) { printf("This version of OpenSSL only supports SHA1 for OAEP, returning\n"); return 0; } #endif } 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; } EVP_PKEY_CTX_free(ctx); EVP_PKEY_free(pkey); 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 || rv == CKR_MECHANISM_PARAM_INVALID) { printf("Mechanism not supported\n"); return 0; } if (rv != CKR_OK) p11_fatal("C_DecryptInit", rv); if ((getCLASS(session, privKeyObject) == CKO_PRIVATE_KEY) && getALWAYS_AUTHENTICATE(session, privKeyObject)) login(session,CKU_CONTEXT_SPECIFIC); data_len = encrypted_len; rv = p11->C_Decrypt(session, encrypted, encrypted_len, data, &data_len); if (rv != CKR_OK) p11_fatal("C_Decrypt", rv); 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 < in_len; n++) printf(" %02x", orig_data[n]); printf("\n"); printf(" Decrypted:"); for (n = 0; n < data_len; n++) printf(" %02x", data[n]); printf("\n"); return 1; } printf("OK\n"); return 0; } #endif /* * Test decryption functions */ static int test_decrypt(CK_SESSION_HANDLE sess) { int errors = 0; CK_RV rv; unsigned char *id; CK_OBJECT_HANDLE pubKeyObject, privKeyObject; CK_MECHANISM_TYPE *mechs = NULL; CK_SESSION_INFO sessionInfo; CK_ULONG num_mechs = 0, id_len; int j; #ifdef ENABLE_OPENSSL CK_ULONG n; #endif char *label; rv = p11->C_GetSessionInfo(sess, &sessionInfo); if (rv != CKR_OK) p11_fatal("C_OpenSession", rv); if (!(sessionInfo.state & CKS_RW_USER_FUNCTIONS)) { printf("Decryption: not a R/W session, skipping decryption tests\n"); return errors; } num_mechs = get_mechanisms(sessionInfo.slotID, &mechs, CKF_DECRYPT); if (num_mechs == 0) { printf("Decrypt: not implemented\n"); return errors; } printf("Decryption (currently only for RSA)\n"); for (j = 0; find_object(sess, CKO_PRIVATE_KEY, &privKeyObject, NULL, 0, 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 (!getDECRYPT(sess, privKeyObject)) { 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, 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++) { switch (mechs[n]) { case CKM_RSA_PKCS_OAEP: /* one more OAEP test with param .. */ errors += encrypt_decrypt(sess, mechs[n], privKeyObject, "ABC", 3); /* 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 } free(mechs); return errors; } static int test_random(CK_SESSION_HANDLE session) { CK_BYTE buf1[100], buf2[100]; CK_BYTE seed1[100]; CK_RV rv; int errors = 0; printf("C_SeedRandom() and C_GenerateRandom():\n"); rv = p11->C_SeedRandom(session, seed1, 100); if (rv == CKR_RANDOM_NO_RNG) { printf(" RNG not available\n"); return 0; } if (rv == CKR_RANDOM_SEED_NOT_SUPPORTED || rv == CKR_FUNCTION_NOT_SUPPORTED) printf(" seeding (C_SeedRandom) not supported\n"); else if (rv != CKR_OK) { p11_perror("C_SeedRandom", rv); errors++; } rv = p11->C_GenerateRandom(session, buf1, 10); if (rv != CKR_OK) { p11_perror("C_GenerateRandom", rv); errors++; } rv = p11->C_GenerateRandom(session, buf1, 100); if (rv != CKR_OK) { p11_perror("C_GenerateRandom(buf1,100)", rv); errors++; } rv = p11->C_GenerateRandom(session, buf1, 0); if (rv != CKR_OK) { p11_perror("C_GenerateRandom(buf1,0)", rv); errors++; } rv = p11->C_GenerateRandom(session, buf2, 100); if (rv != CKR_OK) { p11_perror("C_GenerateRandom(buf2,100)", rv); errors++; } if (errors == 0 && memcmp(buf1, buf2, 100) == 0) { printf(" ERR: C_GenerateRandom returned twice the same value!!!\n"); errors++; } if (!errors) printf(" seems to be OK\n"); return errors; } static int test_card_detection(int wait_for_event) { char buffer[256]; CK_SLOT_ID slot_id; CK_RV rv; printf("Testing card detection using %s\n", wait_for_event? "C_WaitForSlotEvent()" : "C_GetSlotList()"); while (1) { printf("Please press return to continue, x to exit: "); fflush(stdout); if (fgets(buffer, sizeof(buffer), stdin) == NULL || buffer[0] == 'x') break; if (wait_for_event) { printf("Calling C_WaitForSlotEvent: "); fflush(stdout); rv = p11->C_WaitForSlotEvent(0, &slot_id, NULL); if (rv != CKR_OK) { printf("failed.\n"); p11_perror("C_WaitForSlotEvent", rv); return 1; } printf("event on slot 0x%lx\n", slot_id); } list_slots(0, 1, 1); } return 0; } static int p11_test(CK_SESSION_HANDLE session) { int errors = 0; errors += test_random(session); errors += test_digest(session); errors += test_cipher(session); errors += test_signature(session); errors += test_verify(session); errors += test_unwrap(session); errors += test_decrypt(session); if (errors == 0) printf("No errors\n"); else printf("%d errors\n", errors); return errors; } /* Does about the same as Mozilla does when you go to an on-line CA * for obtaining a certificate: key pair generation, signing the * cert request + some other tests, writing certs and changing * some attributes. */ static CK_SESSION_HANDLE test_kpgen_certwrite(CK_SLOT_ID slot, CK_SESSION_HANDLE session) { CK_MECHANISM mech = {CKM_RSA_PKCS, NULL_PTR, 0}; CK_MECHANISM_TYPE *mech_type = NULL; CK_OBJECT_HANDLE pub_key, priv_key; CK_ULONG i, num_mechs = 0; CK_RV rv; CK_BYTE buf[20], *tmp; CK_BYTE md5_and_digestinfo[34] = "\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05\x05\x00\x04\x10"; CK_BYTE *data, sig[512]; CK_ULONG data_len, sig_len; CK_BYTE id[] = "abcdefghijklmnopqrst"; CK_ULONG id_len = 20, mod_len = 0; CK_BYTE *label = (CK_BYTE *) "Just a label"; CK_ULONG label_len = 12; CK_ATTRIBUTE attribs[3] = { {CKA_ID, id, id_len}, {CKA_LABEL, label, label_len}, {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"); return session; } if (!opt_key_type) { printf("ERR: must give an RSA key type, e.g.: --key-type RSA:1024\n"); return session; } printf("\n*** We already opened a session and logged in ***\n"); num_mechs = get_mechanisms(slot, &mech_type, -1); for (i = 0; i < num_mechs; i++) { if (mech_type[i] == CKM_RSA_PKCS_KEY_PAIR_GEN) break; } if (i == num_mechs) { fprintf(stderr, "ERR: no \"CKM_RSA_PKCS_KEY_PAIR_GEN\" found in the mechanism list\n"); return session; } f = fopen(opt_file_to_write, "rb"); if (f == NULL) util_fatal("Couldn't open file \"%s\"", opt_file_to_write); fclose(f); /* Get for a not-yet-existing ID */ while(find_object(session, CKO_PRIVATE_KEY, &priv_key, id, id_len, 0)) id[0]++; printf("\n*** Generating a %s key pair ***\n", opt_key_type); if (!gen_keypair(slot, session, &pub_key, &priv_key, opt_key_type)) { printf("ERR: cannot generate new key pair\n"); return session; } 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; 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 reasonable limits */ fprintf(stderr, "ERR: GetAttribute(privkey, CKA_MODULUS) doesn't seem to work\n"); return session; } printf("\n*** Changing the CKA_ID of private and public key into one of 20 bytes ***\n"); rv = p11->C_SetAttributeValue(session, priv_key, attribs, 1); if (rv != CKR_OK) p11_fatal("C_SetAttributeValue(priv_key)", rv); rv = p11->C_SetAttributeValue(session, pub_key, attribs, 1); if (rv != CKR_OK) p11_fatal("C_SetAttributeValue(pub_key)", rv); printf("\n*** Do a signature and verify it (presumably to test the keys) ***\n"); data = buf; data_len = 20; rv = p11->C_SignInit(session, &mech, priv_key); if (rv != CKR_OK) p11_fatal("C_SignInit", rv); 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) p11_fatal("C_Sign", rv); sig_len = 20; rv = p11->C_Sign(session, data, data_len, sig, &sig_len); if (rv != CKR_BUFFER_TOO_SMALL) { fprintf(stderr, "ERR: C_Sign() didn't return CKR_BUFFER_TO_SMALL but %s\n", CKR2Str(rv)); return session; } rv = p11->C_Sign(session, data, data_len, sig, &sig_len); if (rv != CKR_OK) p11_fatal("C_Sign", rv); rv = p11->C_VerifyInit(session, &mech, pub_key); if (rv != CKR_OK) p11_fatal("C_VerifyInit", rv); rv = p11->C_Verify(session, data, data_len, sig, sig_len); if (rv != CKR_OK) p11_fatal("C_Verify", rv); /* Sign the certificate request */ printf("\n*** Signing the certificate request ***\n"); data = md5_and_digestinfo; data_len = 20; rv = p11->C_SignInit(session, &mech, priv_key); if (rv != CKR_OK) p11_fatal("C_SignInit", rv); 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); printf("\n*** Changing the CKA_LABEL, CKA_ID and CKA_SUBJECT of the public key ***\n"); rv = p11->C_SetAttributeValue(session, pub_key, attribs, 3); if (rv != CKR_OK) p11_fatal("C_SetAttributeValue", rv); printf("*** Deleting the private and the public key again ***\n"); rv = p11->C_DestroyObject(session, priv_key); if (rv != CKR_OK) p11_fatal("C_DestroyObject()", rv); rv = p11->C_DestroyObject(session, pub_key); if (rv != CKR_OK) p11_fatal("C_DestroyObject()", rv); printf("\n*** Logging off and releasing pkcs11 lib ***\n"); rv = p11->C_CloseAllSessions(slot); if (rv != CKR_OK) p11_fatal("CloseAllSessions", rv); rv = p11->C_Finalize(NULL); if (rv != CKR_OK) p11_fatal("Finalize", rv); C_UnloadModule(module); /* Now we assume the user turns of her PC and comes back tomorrow to see * 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_v2); if (module == NULL) util_fatal("Failed to load pkcs11 module"); p11 = (CK_FUNCTION_LIST_3_0_PTR ) p11_v2; 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) p11_fatal("C_Initialize", rv); rv = p11->C_OpenSession(opt_slot, CKF_SERIAL_SESSION| CKF_RW_SESSION, NULL, NULL, &session); if (rv != CKR_OK) p11_fatal("C_OpenSession", rv); login(session, CKU_USER); printf("\n*** Put a cert on the card (NOTE: doesn't correspond with the key!) ***\n"); opt_object_class = CKO_CERTIFICATE; memcpy(opt_object_id, id, id_len); opt_object_id_len = id_len; opt_object_label = (char *) label; if (!write_object(session)) util_fatal("Failed to write certificate"); if (!delete_object(session)) util_fatal("Failed to delete certificate"); printf("\n==> OK, successful! Should work with Mozilla\n"); return session; } static void test_ec(CK_SLOT_ID slot, CK_SESSION_HANDLE session) { CK_MECHANISM mech = {CKM_ECDSA_SHA1, NULL_PTR, 0}; CK_MECHANISM_TYPE *mech_type = NULL; CK_OBJECT_HANDLE pub_key, priv_key; CK_ULONG i, num_mechs = 0; CK_RV rv; CK_BYTE *tmp; CK_BYTE *data_to_sign = (CK_BYTE *)"My Heart's in the Highland"; CK_BYTE *data, sig[512]; CK_ULONG data_len, sig_len; CK_BYTE *id = (CK_BYTE *) "abcdefghijklmnopqrst"; CK_ULONG id_len = strlen((char *)id), ec_params_len, ec_point_len; CK_BYTE *label = (CK_BYTE *) "Just a label"; CK_ULONG label_len = 12; CK_ATTRIBUTE attribs[3] = { {CKA_ID, id, id_len}, {CKA_LABEL, label, label_len}, {CKA_SUBJECT, (void *) "This won't be used in our lib", 29} }; if (!opt_object_id_len) { fprintf(stderr, "ERR: must give an ID, e.g.: --id 01\n"); return; } if (!opt_key_type) { fprintf(stderr, "ERR: must give an EC key type, e.g.: --key-type EC:secp256r1\n"); return; } printf("\n*** We already opened a session and logged in ***\n"); num_mechs = get_mechanisms(slot, &mech_type, -1); for (i = 0; i < num_mechs; i++) if (mech_type[i] == CKM_EC_KEY_PAIR_GEN) break; if (i == num_mechs) { printf("warning: no 'CKM_EC_KEY_PAIR_GEN' found in the mechanism list\n"); //return; } printf("*** Generating EC key pair ***\n"); if (!gen_keypair(slot, session, &pub_key, &priv_key, opt_key_type)) return; tmp = getID(session, priv_key, &i); if (i == 0) { printf("ERR: newly generated private key has no (or an empty) CKA_ID\n"); return; } i = (size_t) opt_object_id_len; memcpy(opt_object_id, tmp, opt_object_id_len); /* This is done in NSS */ getEC_PARAMS(session, priv_key, &ec_params_len); if (ec_params_len < 5 || ec_params_len > 10000) { printf("ERR: GetAttribute(privkey, CKA_EC_PARAMS) doesn't seem to work\n"); return; } getEC_POINT(session, pub_key, &ec_point_len); if (ec_point_len < 5 || ec_point_len > 10000) { printf("ERR: GetAttribute(pubkey, CKA_EC_POINT) doesn't seem to work\n"); return; } printf("*** Changing the CKA_ID of private and public key into one of 20 bytes ***\n"); rv = p11->C_SetAttributeValue(session, priv_key, attribs, 1); if (rv != CKR_OK) p11_warn("C_SetAttributeValue(priv_key)", rv); rv = p11->C_SetAttributeValue(session, pub_key, attribs, 1); if (rv != CKR_OK) p11_warn("C_SetAttributeValue(pub_key)", rv); printf("*** Doing a signature ***\n"); data = data_to_sign; data_len = strlen((char *)data_to_sign); rv = p11->C_SignInit(session, &mech, priv_key); if (rv != CKR_OK) p11_fatal("C_SignInit", rv); 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) p11_fatal("C_Sign", rv); sig_len -= 20; rv = p11->C_Sign(session, data, data_len, sig, &sig_len); if (rv != CKR_BUFFER_TOO_SMALL) { printf("warning: C_Sign() didn't return CKR_BUFFER_TO_SMALL but %s\n", CKR2Str(rv)); // return; } sig_len += 20; // re-doing C_SignInit after C_SignFinal to avoid CKR_OPERATION_NOT_INITIALIZED for CardOS rv = p11->C_SignFinal(session, sig, &sig_len); if (rv != CKR_OK) { p11_warn("C_SignFinal", rv); } rv = p11->C_SignInit(session, &mech, priv_key); if (rv != CKR_OK) p11_fatal("C_SignInit", rv); 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); printf("*** Changing the CKA_LABEL, CKA_ID and CKA_SUBJECT of the public key ***\n"); rv = p11->C_SetAttributeValue(session, pub_key, attribs, 3); if (rv != CKR_OK) p11_warn("C_SetAttributeValue(pub_key)", rv); printf("*** Deleting the private and the public key again ***\n"); rv = p11->C_DestroyObject(session, priv_key); if (rv != CKR_OK) p11_fatal("C_DestroyObject()", rv); rv = p11->C_DestroyObject(session, pub_key); if (rv != CKR_OK) p11_fatal("C_DestroyObject()", rv); printf("==> OK\n"); } #ifndef _WIN32 static void test_fork(void) { CK_RV rv; pid_t pid = fork(); if (!pid) { printf("*** Calling C_Initialize in forked child process ***\n"); rv = p11->C_Initialize(c_initialize_args_ptr); if (rv != CKR_OK) p11_fatal("C_Initialize in child\n", rv); exit(0); } else if (pid < 0) { util_fatal("Failed to fork for test: %s", strerror(errno)); } else { int st; waitpid(pid, &st, 0); if (!WIFEXITED(st) || WEXITSTATUS(st)) util_fatal("Child process exited with status %d", st); } } #endif static void generate_random(CK_SESSION_HANDLE session) { CK_RV rv; CK_BYTE *buf; FILE *out; buf = malloc(opt_random_bytes); if (!buf) util_fatal("Not enough memory to allocate random data buffer"); rv = p11->C_GenerateRandom(session, buf, opt_random_bytes); if (rv != CKR_OK) util_fatal("Could not generate random bytes"); if (opt_output) { out = fopen(opt_output, "wb"); if (out==NULL) util_fatal("Cannot open '%s'", opt_output); } else out = stdout; if (fwrite(buf, 1, opt_random_bytes, out) != opt_random_bytes) util_fatal("Cannot write to '%s'", opt_output); if (opt_output) fclose(out); free(buf); } static const char *p11_flag_names(struct flag_info *list, CK_FLAGS value) { static char buffer[1024]; const char *sepa = ""; buffer[0] = '\0'; while (list->value) { if (list->value & value) { strlcat(buffer, sepa, sizeof buffer); strlcat(buffer, list->name, sizeof buffer); value &= ~list->value; sepa = ", "; } list++; } if (value) { sprintf(buffer+strlen(buffer), "%sother flags=0x%x", sepa, (unsigned int) value); } return buffer; } static const char *p11_slot_info_flags(CK_FLAGS value) { static struct flag_info slot_flags[] = { { CKF_TOKEN_PRESENT, "token present" }, { CKF_REMOVABLE_DEVICE, "removable device" }, { CKF_HW_SLOT, "hardware slot" }, { 0, NULL } }; return p11_flag_names(slot_flags, value); } static const char *p11_token_info_flags(CK_FLAGS value) { static struct flag_info slot_flags[] = { { CKF_LOGIN_REQUIRED, "login required" }, { CKF_PROTECTED_AUTHENTICATION_PATH, "PIN pad present" }, { CKF_RNG, "rng" }, { CKF_SO_PIN_COUNT_LOW, "SO PIN count low" }, { CKF_SO_PIN_FINAL_TRY, "final SO PIN try" }, { CKF_SO_PIN_LOCKED, "SO PIN locked" }, { CKF_SO_PIN_TO_BE_CHANGED, "SO PIN to be changed"}, { CKF_TOKEN_INITIALIZED, "token initialized" }, { CKF_USER_PIN_COUNT_LOW, "user PIN count low" }, { CKF_USER_PIN_FINAL_TRY, "final user PIN try" }, { CKF_USER_PIN_INITIALIZED, "PIN initialized" }, { CKF_USER_PIN_LOCKED, "user PIN locked" }, { CKF_USER_PIN_TO_BE_CHANGED, "user PIN to be changed"}, { CKF_WRITE_PROTECTED, "readonly" }, { 0, NULL } }; return p11_flag_names(slot_flags, value); } static const char *p11_utf8_to_local(CK_UTF8CHAR *string, size_t len) { static char buffer[512]; size_t n, m; while (len && string[len-1] == ' ') len--; /* For now, simply copy this thing */ for (n = m = 0; n < sizeof(buffer) - 1; 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) p11->C_Finalize(NULL_PTR); if (module) C_UnloadModule(module); util_fatal("PKCS11 function %s failed: rv = %s (0x%0x)", func, CKR2Str(rv), (unsigned int) rv); } static void p11_warn(const char *func, CK_RV rv) { if (!suppress_warn) util_warn("PKCS11 function %s failed: rv = %s (0x%0x)\n", func, CKR2Str(rv), (unsigned int) rv); } 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); } #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) { CK_BYTE_PTR array; size_t size = 0; /* no hex string supplied on command line */ if (!hex_input) { *input_size = 0; return NULL; } /* 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; } size = *input_size; 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; } 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) { size_t i = 0; /* initialization with some data */ while (i < dataLen) { *data = rand() & 0xFF; data++; i++; } } // clang-format off static struct mech_info p11_mechanisms[] = { { 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}, { 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) { static char temp[64]; struct mech_info *mi; for (mi = p11_mechanisms; mi->name; mi++) { if (mi->mech == mech) return mi->name; } snprintf(temp, sizeof(temp), "mechtype-0x%lX", (unsigned long) 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))) return mi->mech; } util_fatal("Unknown PKCS11 mechanism \"%s\"", 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) { case CKR_OK: return "CKR_OK"; case CKR_CANCEL: return "CKR_CANCEL"; case CKR_HOST_MEMORY: return "CKR_HOST_MEMORY"; case CKR_SLOT_ID_INVALID: return "CKR_SLOT_ID_INVALID"; case CKR_GENERAL_ERROR: return "CKR_GENERAL_ERROR"; case CKR_FUNCTION_FAILED: return "CKR_FUNCTION_FAILED"; case CKR_ARGUMENTS_BAD: return "CKR_ARGUMENTS_BAD"; case CKR_NO_EVENT: return "CKR_NO_EVENT"; case CKR_NEED_TO_CREATE_THREADS: return "CKR_NEED_TO_CREATE_THREADS"; case CKR_CANT_LOCK: return "CKR_CANT_LOCK"; case CKR_ATTRIBUTE_READ_ONLY: return "CKR_ATTRIBUTE_READ_ONLY"; case CKR_ATTRIBUTE_SENSITIVE: return "CKR_ATTRIBUTE_SENSITIVE"; case CKR_ATTRIBUTE_TYPE_INVALID: return "CKR_ATTRIBUTE_TYPE_INVALID"; case CKR_ATTRIBUTE_VALUE_INVALID: return "CKR_ATTRIBUTE_VALUE_INVALID"; case CKR_DATA_INVALID: return "CKR_DATA_INVALID"; case CKR_DATA_LEN_RANGE: return "CKR_DATA_LEN_RANGE"; case CKR_DEVICE_ERROR: return "CKR_DEVICE_ERROR"; case CKR_DEVICE_MEMORY: return "CKR_DEVICE_MEMORY"; case CKR_DEVICE_REMOVED: return "CKR_DEVICE_REMOVED"; case CKR_ENCRYPTED_DATA_INVALID: return "CKR_ENCRYPTED_DATA_INVALID"; case CKR_ENCRYPTED_DATA_LEN_RANGE: return "CKR_ENCRYPTED_DATA_LEN_RANGE"; case CKR_FUNCTION_CANCELED: return "CKR_FUNCTION_CANCELED"; case CKR_FUNCTION_NOT_PARALLEL: return "CKR_FUNCTION_NOT_PARALLEL"; case CKR_FUNCTION_NOT_SUPPORTED: return "CKR_FUNCTION_NOT_SUPPORTED"; case CKR_KEY_HANDLE_INVALID: return "CKR_KEY_HANDLE_INVALID"; case CKR_KEY_SIZE_RANGE: return "CKR_KEY_SIZE_RANGE"; case CKR_KEY_TYPE_INCONSISTENT: return "CKR_KEY_TYPE_INCONSISTENT"; case CKR_KEY_NOT_NEEDED: return "CKR_KEY_NOT_NEEDED"; case CKR_KEY_CHANGED: return "CKR_KEY_CHANGED"; case CKR_KEY_NEEDED: return "CKR_KEY_NEEDED"; case CKR_KEY_INDIGESTIBLE: return "CKR_KEY_INDIGESTIBLE"; case CKR_KEY_FUNCTION_NOT_PERMITTED: return "CKR_KEY_FUNCTION_NOT_PERMITTED"; case CKR_KEY_NOT_WRAPPABLE: return "CKR_KEY_NOT_WRAPPABLE"; case CKR_KEY_UNEXTRACTABLE: return "CKR_KEY_UNEXTRACTABLE"; case CKR_MECHANISM_INVALID: return "CKR_MECHANISM_INVALID"; case CKR_MECHANISM_PARAM_INVALID: return "CKR_MECHANISM_PARAM_INVALID"; case CKR_OBJECT_HANDLE_INVALID: return "CKR_OBJECT_HANDLE_INVALID"; case CKR_OPERATION_ACTIVE: return "CKR_OPERATION_ACTIVE"; case CKR_OPERATION_NOT_INITIALIZED: return "CKR_OPERATION_NOT_INITIALIZED"; case CKR_PIN_INCORRECT: return "CKR_PIN_INCORRECT"; case CKR_PIN_INVALID: return "CKR_PIN_INVALID"; case CKR_PIN_LEN_RANGE: return "CKR_PIN_LEN_RANGE"; case CKR_PIN_EXPIRED: return "CKR_PIN_EXPIRED"; case CKR_PIN_LOCKED: return "CKR_PIN_LOCKED"; case CKR_SESSION_CLOSED: return "CKR_SESSION_CLOSED"; case CKR_SESSION_COUNT: return "CKR_SESSION_COUNT"; case CKR_SESSION_HANDLE_INVALID: return "CKR_SESSION_HANDLE_INVALID"; case CKR_SESSION_PARALLEL_NOT_SUPPORTED: return "CKR_SESSION_PARALLEL_NOT_SUPPORTED"; case CKR_SESSION_READ_ONLY: return "CKR_SESSION_READ_ONLY"; case CKR_SESSION_EXISTS: return "CKR_SESSION_EXISTS"; case CKR_SESSION_READ_ONLY_EXISTS: return "CKR_SESSION_READ_ONLY_EXISTS"; case CKR_SESSION_READ_WRITE_SO_EXISTS: return "CKR_SESSION_READ_WRITE_SO_EXISTS"; case CKR_SIGNATURE_INVALID: return "CKR_SIGNATURE_INVALID"; case CKR_SIGNATURE_LEN_RANGE: return "CKR_SIGNATURE_LEN_RANGE"; case CKR_TEMPLATE_INCOMPLETE: return "CKR_TEMPLATE_INCOMPLETE"; case CKR_TEMPLATE_INCONSISTENT: return "CKR_TEMPLATE_INCONSISTENT"; case CKR_TOKEN_NOT_PRESENT: return "CKR_TOKEN_NOT_PRESENT"; case CKR_TOKEN_NOT_RECOGNIZED: return "CKR_TOKEN_NOT_RECOGNIZED"; case CKR_TOKEN_WRITE_PROTECTED: return "CKR_TOKEN_WRITE_PROTECTED"; case CKR_UNWRAPPING_KEY_HANDLE_INVALID: return "CKR_UNWRAPPING_KEY_HANDLE_INVALID"; case CKR_UNWRAPPING_KEY_SIZE_RANGE: return "CKR_UNWRAPPING_KEY_SIZE_RANGE"; case CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT: return "CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT"; case CKR_USER_ALREADY_LOGGED_IN: return "CKR_USER_ALREADY_LOGGED_IN"; case CKR_USER_NOT_LOGGED_IN: return "CKR_USER_NOT_LOGGED_IN"; case CKR_USER_PIN_NOT_INITIALIZED: return "CKR_USER_PIN_NOT_INITIALIZED"; case CKR_USER_TYPE_INVALID: return "CKR_USER_TYPE_INVALID"; case CKR_USER_ANOTHER_ALREADY_LOGGED_IN: return "CKR_USER_ANOTHER_ALREADY_LOGGED_IN"; case CKR_USER_TOO_MANY_TYPES: return "CKR_USER_TOO_MANY_TYPES"; case CKR_WRAPPED_KEY_INVALID: return "CKR_WRAPPED_KEY_INVALID"; case CKR_WRAPPED_KEY_LEN_RANGE: return "CKR_WRAPPED_KEY_LEN_RANGE"; case CKR_WRAPPING_KEY_HANDLE_INVALID: return "CKR_WRAPPING_KEY_HANDLE_INVALID"; case CKR_WRAPPING_KEY_SIZE_RANGE: return "CKR_WRAPPING_KEY_SIZE_RANGE"; case CKR_WRAPPING_KEY_TYPE_INCONSISTENT: return "CKR_WRAPPING_KEY_TYPE_INCONSISTENT"; case CKR_RANDOM_SEED_NOT_SUPPORTED: return "CKR_RANDOM_SEED_NOT_SUPPORTED"; case CKR_RANDOM_NO_RNG: return "CKR_RANDOM_NO_RNG"; case CKR_DOMAIN_PARAMS_INVALID: return "CKR_DOMAIN_PARAMS_INVALID"; case CKR_BUFFER_TOO_SMALL: return "CKR_BUFFER_TOO_SMALL"; case CKR_SAVED_STATE_INVALID: return "CKR_SAVED_STATE_INVALID"; case CKR_INFORMATION_SENSITIVE: return "CKR_INFORMATION_SENSITIVE"; case CKR_STATE_UNSAVEABLE: return "CKR_STATE_UNSAVEABLE"; case CKR_CRYPTOKI_NOT_INITIALIZED: return "CKR_CRYPTOKI_NOT_INITIALIZED"; case CKR_CRYPTOKI_ALREADY_INITIALIZED: return "CKR_CRYPTOKI_ALREADY_INITIALIZED"; case CKR_MUTEX_BAD: return "CKR_MUTEX_BAD"; case CKR_MUTEX_NOT_LOCKED: return "CKR_MUTEX_NOT_LOCKED"; case CKR_VENDOR_DEFINED: return "CKR_VENDOR_DEFINED"; } 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) */ OpenSC-0.26.1/src/tools/pkcs15-crypt.c000066400000000000000000000317651474147347300173210ustar00rootroot00000000000000/* * pkcs15-crypt.c: Tool for cryptography operations with smart cards * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include #include #ifdef HAVE_UNISTD_H #include #endif #include #include #include "common/compat_getpass.h" #include "libopensc/internal.h" #include "libopensc/opensc.h" #include "libopensc/pkcs15.h" #include "libopensc/asn1.h" #include "util.h" static const char *app_name = "pkcs15-crypt"; static int verbose = 0, opt_wait = 0, opt_raw = 0; static char * opt_reader; static char * opt_pincode = NULL, * opt_key_id = NULL; static char * opt_input = NULL, * opt_output = NULL; static char * opt_bind_to_aid = NULL; static char * opt_sig_format = NULL; static int opt_crypt_flags = 0; enum { OPT_SHA1 = 0x100, OPT_SHA256, OPT_SHA384, OPT_SHA512, OPT_SHA224, OPT_MD5, OPT_PKCS1, OPT_BIND_TO_AID, OPT_VERSION, }; static const struct option options[] = { { "version", 0, NULL, OPT_VERSION }, { "sign", 0, NULL, 's' }, { "decipher", 0, NULL, 'c' }, { "key", 1, NULL, 'k' }, { "reader", 1, NULL, 'r' }, { "input", 1, NULL, 'i' }, { "output", 1, NULL, 'o' }, { "signature-format", 1, NULL, 'f' }, { "raw", 0, NULL, 'R' }, { "sha-1", 0, NULL, OPT_SHA1 }, { "sha-256", 0, NULL, OPT_SHA256 }, { "sha-384", 0, NULL, OPT_SHA384 }, { "sha-512", 0, NULL, OPT_SHA512 }, { "sha-224", 0, NULL, OPT_SHA224 }, { "md5", 0, NULL, OPT_MD5 }, { "pkcs1", 0, NULL, OPT_PKCS1 }, { "pin", 1, NULL, 'p' }, { "aid", 1, NULL, OPT_BIND_TO_AID }, { "wait", 0, NULL, 'w' }, { "verbose", 0, NULL, 'v' }, { NULL, 0, NULL, 0 } }; static const char *option_help[] = { "Print OpenSC package version", "Performs digital signature operation", "Decipher operation", "Selects the private key ID to use", "Uses reader number ", "Selects the input file to use (defaults to stdin)", "Outputs to file (defaults to stdout)", "Format for ECDSA signature : 'rs' (default), 'sequence', 'openssl'", "Outputs raw 8 bit data", "Input file is a SHA-1 hash", "Input file is a SHA-256 hash", "Input file is a SHA-384 hash", "Input file is a SHA-512 hash", "Input file is a SHA-224 hash", "Input file is a MD5 hash", "Use PKCS #1 v1.5 padding", "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, may be used several times", }; static sc_context_t *ctx = NULL; static sc_card_t *card = NULL; static struct sc_pkcs15_card *p15card = NULL; static char *readpin_stdin(void) { char buf[128]; char *p; p = fgets(buf, sizeof(buf), stdin); if (p != NULL) { p = strchr(buf, '\n'); if (p != NULL) *p = '\0'; return strdup(buf); } return NULL; } static char * get_pin(struct sc_pkcs15_object *obj) { char buf[(sizeof obj->label) + 20]; char *pincode; 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; if (opt_pincode != NULL) { if (strcmp(opt_pincode, "-") == 0) return readpin_stdin(); else return strdup(opt_pincode); } snprintf(buf, sizeof(buf), "Enter PIN [%.*s]: ", (int) sizeof obj->label, obj->label); while (1) { pincode = getpass(buf); if (strlen(pincode) == 0) return NULL; if (strlen(pincode) < pinfo->attrs.pin.min_length || strlen(pincode) > pinfo->attrs.pin.max_length) continue; return strdup(pincode); } } static size_t read_input(u8 *buf, int buflen) { FILE *inf; size_t c; if (opt_input==NULL) { inf = stdin; } else { inf = fopen(opt_input, "rb"); if (inf == NULL) { fprintf(stderr, "Unable to open '%s' for reading.\n", opt_input); return 0; } } c = fread(buf, 1, buflen, inf); if (inf!=stdin) { fclose(inf); } if (c <= 0) { perror("read"); return 0; } return c; } static int write_output(const u8 *buf, size_t len) { FILE *outf; int output_binary = (opt_output == NULL && opt_raw == 0 ? 0 : 1); if (opt_output != NULL) { outf = fopen(opt_output, "wb"); if (outf == NULL) { fprintf(stderr, "Unable to open '%s' for writing.\n", opt_output); return -1; } } else { outf = stdout; } if (output_binary == 0) util_print_binary(outf, buf, len); else fwrite(buf, len, 1, outf); if (outf != stdout) fclose(outf); return 0; } 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, 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) return 2; len = sizeof(out); if (obj->type == SC_PKCS15_TYPE_PRKEY_RSA && !(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); return 2; } if (!key->native) { fprintf(stderr, "Deprecated non-native key detected! Upgrade your smart cards.\n"); return SC_ERROR_NOT_SUPPORTED; } 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; } len = r; if (obj->type == SC_PKCS15_TYPE_PRKEY_EC) { 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(ctx, out, len, &seq, &seqlen)) { fprintf(stderr, "Failed to convert signature to ASN1 sequence format.\n"); return 2; } memcpy(out, seq, seqlen); len = seqlen; free(seq); } } r = write_output(out, len); return r; } static int decipher(struct sc_pkcs15_object *obj) { u8 buf[1024], out[1024]; 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) return 2; len = sizeof(out); if (!((struct sc_pkcs15_prkey_info *) obj->data)->native) { fprintf(stderr, "Deprecated non-native key detected! Upgrade your smart cards.\n"); return SC_ERROR_NOT_SUPPORTED; } 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 r; } static int get_key(unsigned int usage, sc_pkcs15_object_t **result) { sc_pkcs15_object_t *key, *pin = NULL; const char *usage_name; sc_pkcs15_id_t id; int r; usage_name = (usage & SC_PKCS15_PRKEY_USAGE_SIGN)? "signature" : "decryption"; if (opt_key_id != NULL) { sc_pkcs15_hex_string_to_id(opt_key_id, &id); r = sc_pkcs15_find_prkey_by_id_usage(p15card, &id, usage, &key); if (r < 0) { fprintf(stderr, "Unable to find private %s key '%s': %s\n", usage_name, opt_key_id, sc_strerror(r)); return 2; } } else { r = sc_pkcs15_find_prkey_by_id_usage(p15card, NULL, usage, &key); if (r < 0) { fprintf(stderr, "Unable to find any private %s key: %s\n", usage_name, sc_strerror(r)); return 2; } } *result = key; if (key->auth_id.len) { static sc_pkcs15_object_t *prev_pin = NULL; char *pincode; r = sc_pkcs15_find_pin_by_auth_id(p15card, &key->auth_id, &pin); if (r) { fprintf(stderr, "Unable to find PIN code for private key: %s\n", sc_strerror(r)); return 1; } /* Pin already verified previously */ if (pin == prev_pin && key->user_consent == 0) return 0; pincode = get_pin(pin); if (((pincode == NULL || *pincode == '\0')) && !(p15card->card->reader->capabilities & SC_READER_CAP_PIN_PAD)) { free(pincode); return 5; } /* * 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)); return 5; } if (verbose) fprintf(stderr, "PIN code correct.\n"); prev_pin = pin; } return 0; } int main(int argc, char *argv[]) { int err = 0, r, c, long_optind = 0; int do_decipher = 0; int do_sign = 0; int do_print_version = 0; int action_count = 0; struct sc_pkcs15_object *key; sc_context_param_t ctx_param; while (1) { c = getopt_long(argc, argv, "sck:r:i:o:f:Rp:vw", options, &long_optind); if (c == -1) break; if (c == '?') { util_print_usage(app_name, options, option_help, NULL); return 2; } switch (c) { case OPT_VERSION: do_print_version = 1; action_count++; break; case 's': do_sign++; action_count++; break; case 'c': do_decipher++; action_count++; break; case 'k': opt_key_id = optarg; action_count++; break; case 'r': opt_reader = optarg; break; case 'i': opt_input = optarg; break; case 'o': opt_output = optarg; break; case 'f': opt_sig_format = optarg; break; case 'R': opt_raw = 1; break; case OPT_SHA1: opt_crypt_flags |= SC_ALGORITHM_RSA_HASH_SHA1; break; case OPT_SHA256: opt_crypt_flags |= SC_ALGORITHM_RSA_HASH_SHA256; break; case OPT_SHA384: opt_crypt_flags |= SC_ALGORITHM_RSA_HASH_SHA384; break; case OPT_SHA512: opt_crypt_flags |= SC_ALGORITHM_RSA_HASH_SHA512; break; case OPT_SHA224: opt_crypt_flags |= SC_ALGORITHM_RSA_HASH_SHA224; break; case OPT_MD5: opt_crypt_flags |= SC_ALGORITHM_RSA_HASH_MD5; break; case OPT_PKCS1: opt_crypt_flags |= SC_ALGORITHM_RSA_PAD_PKCS1; break; case 'v': verbose++; break; case 'p': opt_pincode = optarg; break; case OPT_BIND_TO_AID: opt_bind_to_aid = optarg; break; case 'w': opt_wait = 1; break; } } 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); action_count--; } if (!(opt_crypt_flags & SC_ALGORITHM_RSA_HASHES)) opt_crypt_flags |= SC_ALGORITHM_RSA_HASH_NONE; 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", sc_strerror(r)); return 1; } err = util_connect_card_ex(ctx, &card, opt_reader, opt_wait, 0); if (err) goto end; if (verbose) fprintf(stderr, "Trying to find a PKCS #15 compatible card...\n"); if (opt_bind_to_aid) { struct sc_aid aid; 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); err = 1; goto end; } r = sc_pkcs15_bind(card, &aid, &p15card); } else { r = sc_pkcs15_bind(card, NULL, &p15card); } if (r) { fprintf(stderr, "PKCS #15 binding failed: %s\n", sc_strerror(r)); err = 1; goto end; } if (verbose) fprintf(stderr, "Found %s!\n", p15card->tokeninfo->label); if (do_decipher) { if ((err = get_key(SC_PKCS15_PRKEY_USAGE_DECRYPT|SC_PKCS15_PRKEY_USAGE_UNWRAP, &key)) || (err = decipher(key))) goto end; action_count--; } if (do_sign) { if ((err = get_key(SC_PKCS15_PRKEY_USAGE_SIGN| SC_PKCS15_PRKEY_USAGE_SIGNRECOVER| SC_PKCS15_PRKEY_USAGE_NONREPUDIATION, &key)) || (err = sign(key))) goto end; action_count--; } end: if (p15card) sc_pkcs15_unbind(p15card); if (card) { sc_disconnect_card(card); } if (ctx) sc_release_context(ctx); return err; } OpenSC-0.26.1/src/tools/pkcs15-init.c000066400000000000000000002415371474147347300171230ustar00rootroot00000000000000/* * Initialize Cards according to PKCS#15. * * This is a fill in the blanks sort of exercise. You need a * profile that describes characteristics of your card, and the * application specific layout on the card. This program will * set up the card according to this specification (including * PIN initialization etc) and create the corresponding PKCS15 * structure. * * There are a very few tasks that are too card specific to have * a generic implementation; that is how PINs and keys are stored * on the card. These should be implemented in pkcs15-.c * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include #include #include #include #ifdef HAVE_STRING_H #include #endif #include #include "libopensc/sc-ossl-compat.h" #include #include #include #include #include #include #include #include #include #include #include /* for OPENSSL_NO_EC */ #if OPENSSL_VERSION_NUMBER >= 0x30000000L # include # include #endif #ifndef OPENSSL_NO_EC #include #endif /* OPENSSL_NO_EC */ #include "common/compat_strlcpy.h" #include "libopensc/internal.h" #include "libopensc/cardctl.h" #include "libopensc/pkcs15.h" #include "libopensc/log.h" #include "libopensc/cards.h" #include "libopensc/asn1.h" #include "pkcs15init/pkcs15-init.h" #include "pkcs15init/profile.h" #include "util.h" #undef GET_KEY_ECHO_OFF static const char *app_name = "pkcs15-init"; /* Handle encoding of PKCS15 on the card */ typedef int (*pkcs15_encoder)(sc_context_t *, struct sc_pkcs15_card *, u8 **, size_t *); /* Local functions */ static int open_reader_and_card(char *); static int do_assert_pristine(sc_card_t *); static int do_erase(sc_card_t *, struct sc_profile *); static int do_erase_application(sc_card_t *, struct sc_profile *); static int do_delete_objects(struct sc_profile *, unsigned int myopt_delete_flags); static int do_change_attributes(struct sc_profile *, unsigned int myopt_type); static int do_init_app(struct sc_profile *); static int do_store_pin(struct sc_profile *); static int do_generate_key(struct sc_profile *, const char *); static int do_generate_skey(struct sc_profile *, const char *); static int do_store_private_key(struct sc_profile *); static int do_store_public_key(struct sc_profile *, EVP_PKEY *); static int do_store_secret_key(struct sc_profile *); static int do_store_certificate(struct sc_profile *); static int do_update_certificate(struct sc_profile *); static int do_convert_cert(sc_pkcs15_der_t *, X509 *); static int is_cacert_already_present(struct sc_pkcs15init_certargs *); static int do_finalize_card(sc_card_t *, struct sc_profile *); static int do_read_data_object(const char *name, u8 **out, size_t *outlen, size_t expected); static int do_store_data_object(struct sc_profile *profile); static int do_sanity_check(struct sc_profile *profile); static int init_prkeyargs(struct sc_pkcs15init_prkeyargs *); static int init_skeyargs(struct sc_pkcs15init_skeyargs *); static void init_gost_params(struct sc_pkcs15init_keyarg_gost_params *, EVP_PKEY *); static int get_pin_callback(struct sc_profile *profile, int id, const struct sc_pkcs15_auth_info *info, const char *label, u8 *pinbuf, size_t *pinsize); static int get_key_callback(struct sc_profile *, int method, int reference, const u8 *, size_t, u8 *, size_t *); static int do_read_private_key(const char *, const char *, EVP_PKEY **, X509 **, unsigned int); 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 int verify_pin(struct sc_pkcs15_card *, char *); enum { OPT_PASSPHRASE = 0x100, OPT_PUBKEY, OPT_SECRKEY, OPT_EXTRACTABLE, OPT_INSECURE, OPT_AUTHORITY, OPT_ASSERT_PRISTINE, OPT_SECRET, OPT_SECRKEY_ALGO, OPT_PUBKEY_LABEL, OPT_CERT_LABEL, OPT_APPLICATION_NAME, OPT_APPLICATION_ID, OPT_PUK_ID, OPT_PUK_LABEL, OPT_VERIFY_PIN, OPT_SANITY_CHECK, OPT_BIND_TO_AID, OPT_UPDATE_LAST_UPDATE, OPT_ERASE_APPLICATION, OPT_IGNORE_CA_CERTIFICATES, OPT_UPDATE_EXISTING, OPT_MD_CONTAINER_GUID, OPT_VERSION, OPT_USER_CONSENT, OPT_PIN1 = 0x10000, /* don't touch these values */ OPT_PUK1 = 0x10001, OPT_PIN2 = 0x10002, OPT_PUK2 = 0x10003, OPT_SERIAL = 0x10004, OPT_NO_SOPIN = 0x10005, OPT_USE_PINPAD= 0x10006, OPT_USE_PINPAD_DEPRECATED }; const struct option options[] = { { "version", 0, NULL, OPT_VERSION }, { "erase-card", no_argument, NULL, 'E' }, { "create-pkcs15", no_argument, NULL, 'C' }, { "store-pin", no_argument, NULL, 'P' }, { "generate-key", required_argument, NULL, 'G' }, { "store-private-key", required_argument, NULL, 'S' }, { "store-public-key", required_argument, NULL, OPT_PUBKEY }, { "store-secret-key", required_argument, NULL, OPT_SECRKEY }, { "store-certificate", required_argument, NULL, 'X' }, { "update-certificate", required_argument, NULL, 'U' }, { "store-data", required_argument, NULL, 'W' }, { "delete-objects", required_argument, NULL, 'D' }, { "change-attributes", required_argument, NULL, 'A' }, { "sanity-check", no_argument, NULL, OPT_SANITY_CHECK}, { "erase-application", required_argument, NULL, OPT_ERASE_APPLICATION}, { "reader", required_argument, NULL, 'r' }, { "pin", required_argument, NULL, OPT_PIN1 }, { "puk", required_argument, NULL, OPT_PUK1 }, { "so-pin", required_argument, NULL, OPT_PIN2 }, { "so-puk", required_argument, NULL, OPT_PUK2 }, { "no-so-pin", no_argument, NULL, OPT_NO_SOPIN }, { "serial", required_argument, NULL, OPT_SERIAL }, { "auth-id", required_argument, NULL, 'a' }, { "puk-id", required_argument, NULL, OPT_PUK_ID }, { "verify-pin", no_argument, NULL, OPT_VERIFY_PIN }, { "id", required_argument, NULL, 'i' }, { "label", required_argument, NULL, 'l' }, { "puk-label", required_argument, NULL, OPT_PUK_LABEL }, { "secret-key-algorithm", required_argument, NULL, OPT_SECRKEY_ALGO }, { "public-key-label", required_argument, NULL, OPT_PUBKEY_LABEL }, { "cert-label", required_argument, NULL, OPT_CERT_LABEL }, { "application-name", required_argument, NULL, OPT_APPLICATION_NAME }, { "application-id", required_argument, NULL, OPT_APPLICATION_ID }, { "aid", required_argument, NULL, OPT_BIND_TO_AID }, { "output-file", required_argument, NULL, 'o' }, { "format", required_argument, NULL, 'f' }, { "passphrase", required_argument, NULL, OPT_PASSPHRASE }, { "authority", no_argument, NULL, OPT_AUTHORITY }, { "key-usage", required_argument, NULL, 'u' }, { "finalize", no_argument, NULL, 'F' }, { "update-last-update", no_argument, NULL, OPT_UPDATE_LAST_UPDATE}, { "ignore-ca-certificates",no_argument, NULL, OPT_IGNORE_CA_CERTIFICATES}, { "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' }, { "use-pinpad", no_argument, NULL, OPT_USE_PINPAD }, { "no-prompt", no_argument, NULL, OPT_USE_PINPAD_DEPRECATED }, { "profile", required_argument, NULL, 'p' }, { "card-profile", required_argument, NULL, 'c' }, { "md-container-guid", required_argument, NULL, OPT_MD_CONTAINER_GUID}, { "wait", no_argument, NULL, 'w' }, { "help", no_argument, NULL, 'h' }, { "verbose", no_argument, NULL, 'v' }, /* Hidden options for testing */ { "assert-pristine", no_argument, NULL, OPT_ASSERT_PRISTINE }, { "secret", required_argument, NULL, OPT_SECRET }, { NULL, 0, NULL, 0 } }; static const char * option_help[] = { "Print OpenSC package version", "Erase the smart card", "Creates a new PKCS #15 structure", "Store a new PIN/PUK on the card", "Generate a new key and store it on the card", "Store private key", "Store public key", "Store secret key", "Store an X.509 certificate", "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)", "Card specific sanity check and possibly update procedure", "Erase application with AID ", "Specify which reader to use", "Specify PIN", "Specify unblock PIN", "Specify security officer (SO) PIN", "Specify unblock PIN for SO PIN", "Do not install a SO PIN, and do not prompt for it", "Specify the serial number of the card", "Specify ID of PIN to use/create", "Specify ID of PUK to use/create", "Verify PIN after card binding (use with --auth-id)", "Specify ID of key/certificate", "Specify label of PIN/key", "Specify label of PUK", "Specify secret key algorithm (use with --store-secret-key)", "Specify public key label (use with --generate-key)", "Specify user cert label (use with --store-private-key)", "Specify application name of data object (use with --store-data-object)", "Specify application id of data object (use with --store-data-object)", "Specify AID of the on-card PKCS#15 application to be binded to (in hexadecimal form)", "Output public portion of generated key to file", "Specify key/cert file format: PEM (=default), DER or PKCS12", "Specify passphrase for unlocking secret key", "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' 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", NULL, "Specify the general profile to use", "Specify the card profile to use", "For a new key specify GUID for a MD container", "Wait for card insertion", "Display this message", "Verbose operation, may be used several times", NULL, NULL, }; enum { ACTION_NONE = 0, ACTION_ASSERT_PRISTINE, ACTION_ERASE, ACTION_INIT, ACTION_DELETE_OBJECTS, ACTION_STORE_PIN, ACTION_GENERATE_KEY, ACTION_STORE_PRIVKEY, ACTION_STORE_PUBKEY, ACTION_STORE_SECRKEY, ACTION_STORE_CERT, ACTION_UPDATE_CERT, ACTION_STORE_DATA, ACTION_FINALIZE_CARD, ACTION_CHANGE_ATTRIBUTES, ACTION_SANITY_CHECK, ACTION_UPDATE_LAST_UPDATE, ACTION_ERASE_APPLICATION, ACTION_PRINT_VERSION, ACTION_MAX }; static const char *action_names[] = { "do nothing", "verify that card is pristine", "erase card", "create PKCS #15 meta structure", "delete object(s)", "store PIN", "generate key", "store private key", "store public key", "store secret key", "store certificate", "update certificate", "store data object", "finalizing card", "change attribute(s)", "check card's sanity", "update 'last-update'", "erase application" }; #define MAX_CERTS 4 #define MAX_SECRETS 16 struct secret { int type; int reference; sc_pkcs15_id_t id; unsigned char key[64]; size_t len; }; /* Flags for do_delete_crypto_objects() and do_change_attributes() */ #define SC_PKCS15INIT_TYPE_PRKEY 1 #define SC_PKCS15INIT_TYPE_PUBKEY 2 #define SC_PKCS15INIT_TYPE_CERT 4 #define SC_PKCS15INIT_TYPE_CHAIN (8 | 4) #define SC_PKCS15INIT_TYPE_DATA 16 #define SC_PKCS15INIT_TYPE_SKEY 32 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, opt_insecure = 0, opt_authority = 0, opt_use_pinpad = 0, opt_no_sopin = 0, opt_use_defkeys = 0, opt_wait = 0, opt_verify_pin = 0; static const char * opt_profile = "pkcs15"; static char * opt_card_profile = NULL; static char * opt_infile = NULL; static char * opt_format = NULL; static char * opt_authid = NULL; static char * opt_objectid = NULL; static char * opt_label = NULL; static char * opt_puk_label = NULL; static char * opt_pubkey_label = NULL; static char * opt_secrkey_algo = NULL; static char * opt_cert_label = NULL; static const char * opt_pins[4]; static char * pins[4]; static char * opt_serial = NULL; static const char * opt_passphrase = NULL; static char * opt_newkey = NULL; static char * opt_outkey = NULL; static char * opt_application_id = NULL; static char * opt_application_name = NULL; static char * opt_bind_to_aid = NULL; static char * opt_puk_authid = NULL; static char * opt_md_container_guid = NULL; static unsigned int opt_x509_usage = 0; static unsigned int opt_delete_flags = 0; static unsigned int opt_type = 0; static int ignore_cmdline_pins = 0; static struct secret opt_secrets[MAX_SECRETS]; 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() */ get_key_callback, /* get_key() */ }; /* * Dialog types for get_pin */ #define SC_UI_USAGE_OTHER 0x0000 #define SC_UI_USAGE_NEW_PIN 0x0001 #define SC_UI_USAGE_UNBLOCK_PIN 0x0002 #define SC_UI_USAGE_CHANGE_PIN 0x0003 /* * Dialog flags */ #define SC_UI_PIN_RETYPE 0x0001 /* new pin, retype */ #define SC_UI_PIN_OPTIONAL 0x0002 /* new pin optional */ #define SC_UI_PIN_CHECK_LENGTH 0x0004 /* check pin length */ #define SC_UI_PIN_MISMATCH_RETRY 0x0008 /* retry if new pin mismatch? */ /* Hints passed to get_pin * M marks mandatory fields, * O marks optional fields */ typedef struct sc_ui_hints { const char * prompt; /* M: cmdline prompt */ const char * dialog_name; /* M: dialog name */ unsigned int usage; /* M: usage hint */ unsigned int flags; /* M: flags */ sc_card_t * card; /* M: card handle */ struct sc_pkcs15_card * p15card; /* O: pkcs15 handle */ /* We may not have a pkcs15 object yet when we get * here, but we may have an idea of what it's going to * look like. */ const char * obj_label; /* O: object (PIN) label */ union { struct sc_pkcs15_auth_info *pin; } info; } sc_ui_hints_t; /* * ask user for a pin */ extern int get_pin(sc_ui_hints_t *hints, char **out); static int get_new_pin(sc_ui_hints_t *, const char *, const char *, char **); int main(int argc, char **argv) { struct sc_profile *profile = NULL; unsigned int n; int r = 0; struct sc_pkcs15_card *tmp_p15_data = NULL; #ifdef RANDOM_POOL if (!RAND_load_file(RANDOM_POOL, 32)) util_fatal("Unable to seed random number pool for key generation"); #endif parse_commandline(argc, argv); if (optind != argc) util_print_usage_and_die(app_name, options, option_help, NULL); if (opt_actions == 0) { fprintf(stderr, "No action specified.\n"); util_print_usage_and_die(app_name, options, option_help, NULL); } if (!opt_profile) { fprintf(stderr, "No profile specified.\n"); util_print_usage_and_die(app_name, options, option_help, NULL); } /* Connect to the card */ if (!open_reader_and_card(opt_reader)) return 1; sc_pkcs15init_set_callbacks(&callbacks); /* Bind the card-specific operations and load the 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; } for (n = 0; n < sizeof(pins)/sizeof(pins[0]); n++) { pins[n] = NULL; } for (n = 0; n < ACTION_MAX; n++) { unsigned int action = n; if (!(opt_actions & (1 << action))) continue; if (action != ACTION_ERASE && action != ACTION_INIT && action != ACTION_ASSERT_PRISTINE && g_p15card == NULL) { /* Read the PKCS15 structure from the card */ if (opt_bind_to_aid) { struct sc_aid aid; 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; } 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(g_card, &aid, &g_p15card); } else { r = sc_pkcs15_bind(g_card, NULL, &g_p15card); } if (r) { fprintf(stderr, "PKCS#15 binding failed: %s\n", sc_strerror(r)); break; } /* XXX: should compare card to profile here to make * sure we're not messing things up */ if (verbose) printf("Found %s\n", g_p15card->tokeninfo->label); sc_pkcs15init_set_p15card(profile, g_p15card); if (opt_verify_pin) { r = verify_pin(g_p15card, opt_authid); if (r) { fprintf(stderr, "Failed to verify User PIN : %s\n", sc_strerror(r)); break; } } } if (verbose && action != ACTION_ASSERT_PRISTINE) printf("About to %s.\n", action_names[action]); switch (action) { case ACTION_PRINT_VERSION: printf("%s\n", OPENSC_SCM_REVISION); break; case ACTION_ASSERT_PRISTINE: /* skip printing error message */ if ((r = do_assert_pristine(g_card)) < 0) goto out; continue; case ACTION_ERASE: r = do_erase(g_card, profile); break; case ACTION_INIT: r = do_init_app(profile); break; case ACTION_STORE_PIN: r = do_store_pin(profile); break; case ACTION_STORE_PRIVKEY: r = do_store_private_key(profile); break; case ACTION_STORE_PUBKEY: r = do_store_public_key(profile, NULL); break; case ACTION_STORE_SECRKEY: r = do_store_secret_key(profile); break; case ACTION_STORE_CERT: r = do_store_certificate(profile); break; case ACTION_UPDATE_CERT: r = do_update_certificate(profile); break; case ACTION_STORE_DATA: r = do_store_data_object(profile); break; case ACTION_DELETE_OBJECTS: r = do_delete_objects(profile, opt_delete_flags); break; case ACTION_CHANGE_ATTRIBUTES: r = do_change_attributes(profile, opt_type); break; case ACTION_GENERATE_KEY: r = do_generate_key(profile, opt_newkey); if (r == SC_ERROR_INVALID_ARGUMENTS) r = do_generate_skey(profile, opt_newkey); break; case ACTION_FINALIZE_CARD: r = do_finalize_card(g_card, profile); break; case ACTION_SANITY_CHECK: r = do_sanity_check(profile); break; case ACTION_UPDATE_LAST_UPDATE: profile->dirty = 1; break; case ACTION_ERASE_APPLICATION: r = do_erase_application(g_card, profile); break; default: util_fatal("Action not yet implemented\n"); } if (r < 0) { fprintf(stderr, "Failed to %s: %s\n", action_names[action], sc_strerror(r)); break; } } for (n = 0; n < sizeof(pins)/sizeof(pins[0]); n++) { free(pins[n]); } 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 (g_p15card) { sc_pkcs15_unbind(g_p15card); } if (g_card) { sc_disconnect_card(g_card); } sc_release_context(g_ctx); return r < 0? 1 : 0; } static int open_reader_and_card(char *reader) { int r; sc_context_param_t ctx_param; 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(&g_ctx, &ctx_param); if (r) { util_error("Failed to establish context: %s\n", sc_strerror(r)); return 0; } if (util_connect_card_ex(g_ctx, &g_card, reader, opt_wait, 0)) return 0; return 1; } /* * Make sure there's no pkcs15 structure on the card */ static int do_assert_pristine(sc_card_t *in_card) { sc_path_t path; int r, ok = 1; r = sc_lock(in_card); if (r < 0) goto end; sc_format_path("3F00", &path); r = sc_select_file(in_card, &path, NULL); if (r) goto end; sc_format_path("2F00", &path); r = sc_select_file(in_card, &path, NULL); if (r) goto end; /* For a while only the presence of OpenSC on-card pkcs#15 is checked. TODO: Parse DIR(2F00) to get know if there is some PKCS#15 applications.*/ sc_format_path("5015", &path); r = sc_select_file(in_card, &path, NULL); if (r) goto end; ok = 0; end: sc_unlock(in_card); if (!ok) { fprintf(stderr, "Card not pristine; detected (possibly incomplete) " "PKCS#15 structure\n"); } else if (verbose) { printf("Pristine card.\n"); } return ok ? 0 : -1; } /* algorithm spec parsing */ struct alg_spec { const char *spec; int algorithm; unsigned int keybits; }; static const struct alg_spec alg_types_sym[] = { { "des", SC_ALGORITHM_DES, 64 }, { "3des", SC_ALGORITHM_3DES, 192 }, { "aes", SC_ALGORITHM_AES, 128 }, { NULL, -1, 0 } }; static const struct alg_spec alg_types_asym[] = { { "rsa", SC_ALGORITHM_RSA, 1024 }, { "gost2001", SC_ALGORITHM_GOSTR3410, SC_PKCS15_GOSTR3410_KEYSIZE }, { "ec", SC_ALGORITHM_EC, 0 }, { NULL, -1, 0 } }; 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; char *end; for (i = 0; types[i].spec; i++) { if (!strncasecmp(spec, types[i].spec, strlen(types[i].spec))) { algorithm = types[i].algorithm; *keybits = types[i].keybits; spec += strlen(types[i].spec); break; } } if (algorithm < 0) { util_error("Unknown algorithm \"%s\"", spec); return SC_ERROR_INVALID_ARGUMENTS; } if (*spec == '/' || *spec == '-' || *spec == ':') spec++; if (*spec) { if (isalpha((unsigned char)*spec) && algorithm == SC_ALGORITHM_EC && prkey) { prkey->u.ec.params.named_curve = strdup(spec); } else { *keybits = (unsigned)strtoul(spec, &end, 10); if (*end) { util_error("Invalid number of key bits \"%s\"", spec); return SC_ERROR_INVALID_ARGUMENTS; } } } return algorithm; } /* * Erase card */ static int do_erase(sc_card_t *in_card, struct sc_profile *profile) { int r; struct sc_pkcs15_card *p15card; struct sc_aid aid; struct sc_aid *paid = NULL; p15card = sc_pkcs15_card_new(); p15card->card = in_card; ignore_cmdline_pins++; if (opt_bind_to_aid) { aid.len = sizeof(aid.value); r = sc_hex_to_bin(opt_bind_to_aid, aid.value, &aid.len); if (r < 0) { fprintf(stderr, "Invalid AID value: '%s'\n", opt_bind_to_aid); goto err; } paid = &aid; } r = sc_lock(p15card->card); if (r < 0) goto err; r = sc_pkcs15init_erase_card(p15card, profile, paid); sc_unlock(p15card->card); ignore_cmdline_pins--; err: sc_pkcs15_card_free(p15card); return r; } static int do_erase_application(sc_card_t *in_card, struct sc_profile *profile) { int r; ignore_cmdline_pins--; r = do_erase(in_card, profile); ignore_cmdline_pins++; return r; } static int do_finalize_card(sc_card_t *in_card, struct sc_profile *profile) { int r; r = sc_lock(in_card); if (r < 0) return r; r = sc_pkcs15init_finalize_card(in_card, profile); sc_unlock(in_card); return r; } /* * Initialize pkcs15 application */ static int do_init_app(struct sc_profile *profile) { struct sc_pkcs15init_initargs args; sc_pkcs15_auth_info_t info; sc_ui_hints_t hints; const char *role = "so"; int r, so_puk_disabled = 0; memset(&hints, 0, sizeof(hints)); memset(&info, 0, sizeof(info)); hints.usage = SC_UI_USAGE_NEW_PIN; hints.flags = SC_UI_PIN_RETYPE | SC_UI_PIN_CHECK_LENGTH | SC_UI_PIN_MISMATCH_RETRY; hints.card = g_card; hints.p15card = NULL; hints.info.pin = &info; /* If it's the onepin option, we need the user PIN iso the SO PIN */ if (opt_profile && strstr(opt_profile, "+onepin")) { if (opt_pins[0]) opt_pins[2] = opt_pins[0]; if (opt_pins[1]) opt_pins[3] = opt_pins[1]; } memset(&args, 0, sizeof(args)); sc_pkcs15init_get_pin_info(profile, SC_PKCS15INIT_SO_PIN, &info); if (!(info.attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN)) role = "user"; else hints.flags |= SC_UI_PIN_OPTIONAL; /* SO PIN is always optional */ 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; if (!opt_pins[2] && !opt_use_pinpad && !opt_no_sopin) { r = get_new_pin(&hints, role, "pin", &pins[2]); if (r < 0) goto failed; opt_pins[2] = pins[2]; } if (!so_puk_disabled && opt_pins[2] && !opt_pins[3] && !opt_use_pinpad) { sc_pkcs15init_get_pin_info(profile, SC_PKCS15INIT_SO_PUK, &info); if (!(info.attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN)) role = "user"; hints.flags |= SC_UI_PIN_OPTIONAL; r = get_new_pin(&hints, role, "puk", &pins[3]); if (r < 0) goto failed; opt_pins[3] = pins[3]; } args.so_pin = (const u8 *) opt_pins[2]; if (args.so_pin) args.so_pin_len = strlen((const char *) args.so_pin); if (!so_puk_disabled) { args.so_puk = (const u8 *) opt_pins[3]; if (args.so_puk) args.so_puk_len = strlen((const char *) args.so_puk); } args.serial = (const char *) opt_serial; args.label = opt_label; r = sc_lock(g_card); if (r < 0) return r; 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)); return SC_ERROR_PKCS15INIT; } /* * Store a PIN/PUK pair */ static int do_store_pin(struct sc_profile *profile) { struct sc_pkcs15init_pinargs args; sc_pkcs15_auth_info_t info; sc_ui_hints_t hints; int r; const char *pin_id; memset(&hints, 0, sizeof(hints)); hints.usage = SC_UI_USAGE_NEW_PIN; hints.flags = SC_UI_PIN_RETYPE | SC_UI_PIN_CHECK_LENGTH | SC_UI_PIN_MISMATCH_RETRY; hints.card = g_card; hints.p15card = g_p15card; hints.info.pin = &info; pin_id = opt_objectid ? opt_objectid : opt_authid; if (!pin_id) { util_error("No pin id specified\n"); return SC_ERROR_INVALID_ARGUMENTS; } sc_pkcs15init_get_pin_info(profile, SC_PKCS15INIT_USER_PIN, &info); if (opt_pins[0] == NULL) { if ((r = get_new_pin(&hints, "user", "pin", &pins[0])) < 0) goto failed; opt_pins[0] = pins[0]; } if (*opt_pins[0] == '\0') { util_error("You must specify a PIN\n"); return SC_ERROR_INVALID_ARGUMENTS; } memset(&args, 0, sizeof(args)); sc_pkcs15_format_id(pin_id, &args.auth_id); args.pin = (u8 *) opt_pins[0]; args.pin_len = strlen(opt_pins[0]); args.label = opt_label; if (!(info.attrs.pin.flags & SC_PKCS15_PIN_FLAG_UNBLOCK_DISABLED) && opt_pins[1] == NULL) { sc_pkcs15init_get_pin_info(profile, SC_PKCS15INIT_USER_PUK, &info); hints.flags |= SC_UI_PIN_OPTIONAL; if ((r = get_new_pin(&hints, "user", "puk", &pins[1])) < 0) goto failed; opt_pins[1] = pins[1]; } if (opt_puk_authid && opt_pins[1]) sc_pkcs15_format_id(opt_puk_authid, &args.puk_id); args.puk_label = opt_puk_label; args.puk = (u8 *) opt_pins[1]; args.puk_len = opt_pins[1]? strlen(opt_pins[1]) : 0; r = sc_lock(g_p15card->card); if (r < 0) return r; 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 */ static int do_store_private_key(struct sc_profile *profile) { struct sc_pkcs15init_prkeyargs args; EVP_PKEY *pkey = NULL; X509 *cert[MAX_CERTS]; int r, i, ncerts; if ((r = init_prkeyargs(&args)) < 0) return r; r = do_read_private_key(opt_infile, opt_format, &pkey, cert, MAX_CERTS); if (r < 0) return r; ncerts = r; if (ncerts) { char namebuf[256]; printf("Importing %d certificates:\n", opt_ignore_ca_certs ? 1 : ncerts); for (i = 0; i < ncerts && !(i && opt_ignore_ca_certs); i++) printf(" %d: %s\n", i, X509_NAME_oneline(X509_get_subject_name(cert[i]), namebuf, sizeof(namebuf))); } r = sc_pkcs15_convert_prkey(&args.key, pkey); if (r < 0) { EVP_PKEY_free(pkey); sc_log_openssl(g_ctx); return r; } init_gost_params(&args.params.gost, pkey); if (ncerts) { unsigned int usage; /* tell openssl to cache the extensions */ X509_check_purpose(cert[0], -1, -1); usage = X509_get_key_usage(cert[0]); /* No certificate usage? Assume ordinary * user cert */ if (usage == 0) 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_ * usage bits than the one specified by the cert, * and complain if it does. * If the usage specified on the command line * is more restrictive, use that. */ if (~usage & opt_x509_usage) { fprintf(stderr, "Warning: requested key usage incompatible with " "key usage specified by X.509 certificate\n"); } args.x509_usage = opt_x509_usage? opt_x509_usage : usage; } args.access_flags |= SC_PKCS15_PRKEY_ACCESS_SENSITIVE; 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(g_p15card, profile, &args, NULL); sc_pkcs15_erase_prkey(&(args.key)); if (r < 0) { EVP_PKEY_free(pkey); sc_unlock(g_p15card->card); return r; } /* If there are certificate as well (e.g. when reading the * private key from a PKCS #12 file) store them, too. */ 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; memset(&cargs, 0, sizeof(cargs)); /* Encode the cert */ 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_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 (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(g_p15card, profile, &cargs, NULL); next_cert: if (cargs_label_needs_free) free((char *) cargs.label); free(cargs.der_encoded.value); } /* No certificates - store the public key */ if (ncerts == 0) r = do_store_public_key(profile, pkey); EVP_PKEY_free(pkey); sc_unlock(g_p15card->card); return r; } /* * Check if the CA certificate is already present */ static int is_cacert_already_present(struct sc_pkcs15init_certargs *args) { sc_pkcs15_object_t *objs[32]; sc_pkcs15_cert_info_t *cinfo; sc_pkcs15_cert_t *cert; int i, count, r; 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) continue; if (strncmp(args->label, objs[i]->label, sizeof objs[i]->label)) continue; /* XXX we should also match the usage field here */ /* Compare the DER representation of the certificates */ 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; if (cert->data.len == args->der_encoded.len && !memcmp(cert->data.value, args->der_encoded.value, cert->data.len)) { sc_pkcs15_free_certificate(cert); return 1; } sc_pkcs15_free_certificate(cert); cert=NULL; } return 0; } /* * Store a public key */ static int do_store_public_key(struct sc_profile *profile, EVP_PKEY *pkey) { struct sc_pkcs15init_pubkeyargs args; sc_pkcs15_object_t *dummy; int r = 0; memset(&args, 0, sizeof(args)); if (opt_objectid) sc_pkcs15_format_id(opt_objectid, &args.id); args.label = (opt_pubkey_label != 0 ? opt_pubkey_label : opt_label); args.x509_usage = opt_x509_usage; if (pkey == NULL) { r = do_read_public_key(opt_infile, opt_format, &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(g_p15card->card); if (r < 0) { sc_pkcs15_erase_pubkey(&(args.key)); return r; } r = sc_pkcs15init_store_public_key(g_p15card, profile, &args, &dummy); sc_pkcs15_erase_pubkey(&(args.key)); sc_unlock(g_p15card->card); } return r; } /* * Store a secret key */ static int do_store_secret_key(struct sc_profile *profile) { struct sc_pkcs15init_skeyargs args; unsigned int keybits; int r, algorithm = -1; if (!opt_secrkey_algo) { util_error("Specify secret key algorithm with --secret-key-algorithm"); return SC_ERROR_INVALID_ARGUMENTS; } if ((r = init_skeyargs(&args)) < 0) return r; algorithm = parse_alg_spec(alg_types_sym, opt_secrkey_algo, &keybits, 0); if (algorithm < 0) { util_error("Invalid symmetric key spec: \"%s\"", opt_secrkey_algo); 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) { free(args.key.data); return r; } args.algorithm = algorithm; args.value_len = keybits; args.access_flags |= SC_PKCS15_PRKEY_ACCESS_SENSITIVE; r = sc_lock(g_p15card->card); if (r < 0) { free(args.key.data); return r; } r = sc_pkcs15init_store_secret_key(g_p15card, profile, &args, NULL); sc_unlock(g_p15card->card); free(args.key.data); return r; } /* * Download certificate to card */ static int do_store_certificate(struct sc_profile *profile) { struct sc_pkcs15init_certargs args; X509 *cert = NULL; int r; memset(&args, 0, sizeof(args)); if (opt_update_existing) args.update = 1; if (opt_objectid) sc_pkcs15_format_id(opt_objectid, &args.id); args.label = (opt_cert_label != 0 ? opt_cert_label : opt_label); args.authority = opt_authority; 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(g_p15card->card); if (r < 0) return r; r = sc_pkcs15init_store_certificate(g_p15card, profile, &args, NULL); sc_unlock(g_p15card->card); } if (args.der_encoded.value) free(args.der_encoded.value); return r; } static int 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 = NULL, *newpk = NULL; int oldpk_type, newpk_type; const u8 *ptr; int r; /* Get the public key from the old cert */ ptr = sc_oldcert->data.value; oldcert = d2i_X509(NULL, &ptr, sc_oldcert->data.len); 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); if (r < 0) { X509_free(oldcert); return r; } 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); /* 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 >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER) if (EVP_PKEY_eq(oldpk, newpk) == 1) #else if (EVP_PKEY_cmp(oldpk, newpk) == 1) #endif r = 0; } EVP_PKEY_free(newpk); EVP_PKEY_free(oldpk); X509_free(oldcert); if (r == 0) r = do_convert_cert(newcert_raw, newcert); else util_error("the public keys in the old and new certificate differ"); X509_free(newcert); return r; } /* * Update an existing certificate with a new certificate having * the same public key. */ static int do_update_certificate(struct sc_profile *profile) { sc_pkcs15_id_t id; sc_pkcs15_object_t *obj; sc_pkcs15_cert_info_t *certinfo; 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"); return SC_ERROR_INVALID_ARGUMENTS; } sc_pkcs15_format_id(opt_objectid, &id); 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(g_p15card->card); if (r < 0) return r; certinfo = (sc_pkcs15_cert_info_t *) obj->data; 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; newcert_raw.value = NULL; r = do_read_check_certificate(oldcert, opt_infile, opt_format, &newcert_raw); sc_pkcs15_free_certificate(oldcert); if (r < 0) goto err; 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(g_p15card->card); return r; } /* * Download data object to card */ static int do_store_data_object(struct sc_profile *profile) { struct sc_pkcs15init_dataargs args; unsigned char *data = NULL; size_t datalen; int r=0; memset(&args, 0, sizeof(args)); sc_init_oid(&args.app_oid); if (opt_objectid) sc_pkcs15_format_id(opt_objectid, &args.id); if (opt_authid) sc_pkcs15_format_id(opt_authid, &args.auth_id); args.label = opt_label; args.app_label = opt_application_name ? opt_application_name : "pkcs15-init"; sc_format_oid(&args.app_oid, opt_application_id); if (opt_application_id && (args.app_oid.value[0] == -1)) { util_error("Invalid OID \"%s\"", opt_application_id); return SC_ERROR_INVALID_ARGUMENTS; } r = do_read_data_object(opt_infile, &data, &datalen, 0); if (r >= 0) { /* der_encoded contains the plain data, nothing DER encoded */ args.der_encoded.value = data; args.der_encoded.len = datalen; r = sc_lock(g_p15card->card); if (r < 0) { free(data); return r; } r = sc_pkcs15init_store_data_object(g_p15card, profile, &args, NULL); sc_unlock(g_p15card->card); } free(data); return r; } /* * Run card specific sanity check procedure */ static int do_sanity_check(struct sc_profile *profile) { int r; r = sc_lock(g_p15card->card); if (r < 0) return r; r = sc_pkcs15init_sanity_check(g_p15card, profile); sc_unlock(g_p15card->card); return r; } static int cert_is_root(sc_pkcs15_cert_t *c) { return (c->subject_len == c->issuer_len) && (memcmp(c->subject, c->issuer, c->subject_len) == 0); } /* Check if the cert has a 'sibling' and return it's parent cert. * 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) { sc_pkcs15_cert_t *cert = NULL; sc_pkcs15_object_t *otherobj; sc_pkcs15_cert_t *othercert = NULL; int r; int private_obj; *issuercert = NULL; *has_sibling = 0; *stop = 0; 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; if (cert_is_root(cert)) { *stop = 1; /* root -> no parent and hence no siblings */ goto done; } for (otherobj = myp15card->obj_list; otherobj != NULL; otherobj = otherobj->next) { if ((otherobj == certobj) || !((otherobj->type & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_CERT)) continue; if (othercert) { sc_pkcs15_free_certificate(othercert); othercert=NULL; } 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) && (memcmp(cert->issuer, othercert->subject, cert->issuer_len) == 0)) { /* parent cert found */ *issuercert = otherobj; *stop = cert_is_root(othercert); } else if (!cert_is_root(othercert) && (cert->issuer_len == othercert->issuer_len) && (memcmp(cert->issuer, othercert->issuer, cert->issuer_len) == 0)) { *has_sibling = 1; break; } } done: sc_pkcs15_free_certificate(cert); if (othercert) sc_pkcs15_free_certificate(othercert); return r; } /* 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 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, struct sc_profile *profile, const sc_pkcs15_id_t *id, unsigned int which) { sc_pkcs15_object_t *objs[10]; /* 1 priv + 1 pub + chain of at most 8 certs, should be enough */ int i, r = 0, count = 0, del_cert = 0; if (which & SC_PKCS15INIT_TYPE_PRKEY) { sc_pkcs15_object_t *key_objs[0x10]; r = sc_pkcs15_get_objects(myp15card, SC_PKCS15_TYPE_PRKEY, key_objs, 0x10); if (r < 0) { fprintf(stderr, "Private key enumeration failed: %s\n", sc_strerror(r)); return r; } for (i = 0; i< r; i++) if (sc_pkcs15_compare_id(id, &((struct sc_pkcs15_prkey_info *)key_objs[i]->data)->id)) objs[count++] = key_objs[i]; if (!count) fprintf(stderr, "NOTE: couldn't find privkey %s to delete\n", sc_pkcs15_print_id(id)); } if (which & SC_PKCS15INIT_TYPE_PUBKEY) { if (sc_pkcs15_find_pubkey_by_id(myp15card, id, &objs[count]) != 0) fprintf(stderr, "NOTE: couldn't find pubkey %s to delete\n", sc_pkcs15_print_id(id)); else count++; } if (which & SC_PKCS15INIT_TYPE_CERT) { if (sc_pkcs15_find_cert_by_id(myp15card, id, &objs[count]) != 0) fprintf(stderr, "NOTE: couldn't find cert %s to delete\n", sc_pkcs15_print_id(id)); else { count++; del_cert = 1; } } if (which & SC_PKCS15INIT_TYPE_SKEY) { if (sc_pkcs15_find_skey_by_id(myp15card, id, &objs[count]) != 0) fprintf(stderr, "NOTE: couldn't find secrkey %s to delete\n", sc_pkcs15_print_id(id)); else count++; } if (del_cert && ((which & SC_PKCS15INIT_TYPE_CHAIN) == SC_PKCS15INIT_TYPE_CHAIN)) { /* Get the cert chain, stop if there's a CA that is the issuer of * other certs on this card */ int has_sibling; /* siblings: certs having the same issuer */ int stop; for( ; count < 10 ; count++) { r = get_cert_info(myp15card, objs[count - 1], &has_sibling, &stop, &objs[count]); if (r < 0) fprintf(stderr, "get_cert_info() failed: %s\n", sc_strerror(r)); else if (has_sibling) fprintf(stderr, "Chain deletion stops with cert %s\n", sc_pkcs15_print_id( &((sc_pkcs15_cert_info_t *) objs[count - 1]->data)->id)); else if (stop && (objs[count] != NULL)) count++; if (stop || (objs[count] == NULL)) break; } if (r < 0) count = -1; /* Something wrong -> don't delete anything */ } for (i = 0; i < count; i++) { r = sc_pkcs15init_delete_object(myp15card, profile, objs[i]); if (r < 0) { fprintf(stderr, "Failed to delete object %d: %s\n", i, sc_strerror(r)); break; } } return r < 0 ? r : count; } static int do_delete_objects(struct sc_profile *profile, unsigned int myopt_delete_flags) { int r = 0, count = 0; r = sc_lock(g_p15card->card); if (r < 0) return r; if (myopt_delete_flags & SC_PKCS15INIT_TYPE_DATA) { struct sc_object_id app_oid; sc_pkcs15_object_t *obj = NULL; if (opt_application_id != NULL) { sc_format_oid(&app_oid, opt_application_id); 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(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(g_p15card, profile, obj); if (r >= 0) count++; } } if (myopt_delete_flags & (SC_PKCS15INIT_TYPE_PRKEY | SC_PKCS15INIT_TYPE_PUBKEY | SC_PKCS15INIT_TYPE_CHAIN | SC_PKCS15INIT_TYPE_SKEY)) { sc_pkcs15_id_t id; if (opt_objectid == NULL) 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(g_p15card, profile, &id, myopt_delete_flags); if (r >= 0) count += r; } sc_unlock(g_p15card->card); printf("Deleted %d objects\n", count); return r; } static int do_change_attributes(struct sc_profile *profile, unsigned int myopt_type) { int r = 0; sc_pkcs15_id_t id; sc_pkcs15_object_t *obj = NULL; if (opt_objectid == NULL) { printf("You have to specify the --id of the object\n"); return 0; } sc_pkcs15_format_id(opt_objectid, &id); /* Right now, only changing the label is supported */ if (opt_label == NULL) { printf("You should specify a --label\n"); return 0; } switch(myopt_type) { case SC_PKCS15INIT_TYPE_PRKEY: 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(g_p15card, &id, &obj)) != 0) return r; break; case SC_PKCS15INIT_TYPE_CERT: 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(g_p15card, &id, &obj)) != 0) return r; break; case SC_PKCS15INIT_TYPE_SKEY: if ((r = sc_pkcs15_find_skey_by_id(g_p15card, &id, &obj)) != 0) return r; break; } if (obj == NULL) { printf("No object of the specified type and with id = \"%s\" found\n", opt_objectid); return 0; } if (opt_label != NULL) { strlcpy(obj->label, opt_label, sizeof(obj->label)); } r = sc_lock(g_p15card->card); if (r < 0) return r; r = sc_pkcs15init_update_any_df(g_p15card, profile, obj->df, 0); sc_unlock(g_p15card->card); return r; } /* * Generate a new private key */ static int do_generate_key(struct sc_profile *profile, const char *spec) { struct sc_pkcs15init_keygen_args keygen_args; unsigned int keybits = 0; int r, algorithm = -1; memset(&keygen_args, 0, sizeof(keygen_args)); keygen_args.pubkey_label = opt_pubkey_label; if ((r = init_prkeyargs(&keygen_args.prkey_args)) < 0) return r; algorithm = parse_alg_spec(alg_types_asym, spec, &keybits, &keygen_args.prkey_args.key); if (algorithm < 0) { util_error("Invalid key spec: \"%s\"", spec); return SC_ERROR_INVALID_ARGUMENTS; } keygen_args.prkey_args.key.algorithm = algorithm; 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; switch (algorithm) { case SC_ALGORITHM_GOSTR3410: /* FIXME: now only SC_PKCS15_PARAMSET_GOSTR3410_A */ keygen_args.prkey_args.params.gost.gostr3410 = SC_PKCS15_PARAMSET_GOSTR3410_A; break; } r = sc_lock(g_p15card->card); if (r == 0) 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; } /* * Generate a new secret key */ static int do_generate_skey(struct sc_profile *profile, const char *spec) { struct sc_pkcs15init_skeyargs skey_args; unsigned int keybits; int r, algorithm = -1; if ((r = init_skeyargs(&skey_args)) < 0) return r; 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(g_p15card->card); if (r == 0) r = sc_pkcs15init_generate_secret_key(g_p15card, profile, &skey_args, NULL); sc_unlock(g_p15card->card); return r; } static int init_prkeyargs(struct sc_pkcs15init_prkeyargs *args) { memset(args, 0, sizeof(*args)); if (opt_objectid) sc_pkcs15_format_id(opt_objectid, &args->id); if (opt_authid) { sc_pkcs15_format_id(opt_authid, &args->auth_id); } else if (!opt_insecure) { util_error("no PIN given for key - either use --insecure or \n" "specify a PIN using --auth-id"); return SC_ERROR_INVALID_ARGUMENTS; } if (opt_extractable) { args->access_flags |= SC_PKCS15_PRKEY_ACCESS_EXTRACTABLE; } args->label = opt_label; args->x509_usage = opt_x509_usage; if (opt_md_container_guid) { args->guid = (unsigned char *)opt_md_container_guid; args->guid_len = strlen(opt_md_container_guid); } args->user_consent = opt_user_consent; return 0; } static int init_skeyargs(struct sc_pkcs15init_skeyargs *args) { memset(args, 0, sizeof(*args)); if (opt_objectid) sc_pkcs15_format_id(opt_objectid, &args->id); if (opt_authid) { sc_pkcs15_format_id(opt_authid, &args->auth_id); } else if (!opt_insecure) { util_error("no PIN given for key - either use --insecure or \n" "specify a PIN using --auth-id"); return SC_ERROR_INVALID_ARGUMENTS; } if (opt_extractable) { args->access_flags |= SC_PKCS15_PRKEY_ACCESS_EXTRACTABLE; } 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 !defined(OPENSSL_NO_EC) #if OPENSSL_VERSION_NUMBER < 0x30000000L EC_KEY *key; #else char name[256]; #endif int nid = NID_undef; assert(pkey); if (EVP_PKEY_id(pkey) == NID_id_GostR3410_2001) { assert(params); #if OPENSSL_VERSION_NUMBER < 0x30000000L key = EVP_PKEY_get0(pkey); assert(key); assert(EC_KEY_get0_group(key)); nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(key)); #else assert(EVP_PKEY_get_group_name(pkey, name, sizeof(name), NULL)); nid = OBJ_txt2nid(name); #endif assert(nid > 0); switch (nid) { case NID_id_GostR3410_2001_CryptoPro_A_ParamSet: params->gostr3410 = SC_PKCS15_PARAMSET_GOSTR3410_A; break; case NID_id_GostR3410_2001_CryptoPro_B_ParamSet: params->gostr3410 = SC_PKCS15_PARAMSET_GOSTR3410_B; break; case NID_id_GostR3410_2001_CryptoPro_C_ParamSet: params->gostr3410 = SC_PKCS15_PARAMSET_GOSTR3410_C; break; } } #else (void)params, (void)pkey; /* no warning */ #endif } /* * Intern secrets given on the command line (mostly for testing) */ static void parse_secret(struct secret *secret, const char *arg) { char *copy, *str, *value; size_t len; str = copy = strdup(arg); if (!(value = strchr(str, '='))) goto parse_err; *value++ = '\0'; if (*str == '@') { sc_pkcs15_format_id(str+1, &secret->id); secret->type = SC_AC_CHV; secret->reference = -1; } else { if (strncasecmp(str, "chv", 3)) secret->type = SC_AC_CHV; else if (strncasecmp(str, "aut", 3)) secret->type = SC_AC_AUT; else if (strncasecmp(str, "pro", 3)) secret->type = SC_AC_PRO; else goto parse_err; str += 3; if (!isdigit((unsigned char)str[3])) goto parse_err; secret->reference = (int)strtoul(str, &str, 10); if (*str != '\0') goto parse_err; } if ((len = strlen(value)) < 3 || value[2] != ':') { memcpy(secret->key, value, len); } else { len = sizeof(secret->key); if (sc_hex_to_bin(value, secret->key, &len) < 0) goto parse_err; } secret->len = len; free(copy); return; parse_err: util_fatal("Cannot parse secret \"%s\"\n", arg); } /* * Prompt for a new PIN * @role can be "user" or "so" * @usage can be "pin" or "puk" */ static int get_new_pin(sc_ui_hints_t *hints, const char *role, const char *usage, char **retstr) { char name[32], prompt[64], label[64]; snprintf(name, sizeof(name), "pkcs15init.new_%s_%s", role, usage); if (!strcmp(role, "user")) role = "User"; else role = "Security Officer"; if (!strcmp(usage, "pin")) { snprintf(prompt, sizeof(prompt), "New %s PIN", role); snprintf(label, sizeof(label), "%s PIN", role); } else { snprintf(prompt, sizeof(prompt), "Unblock Code for New %s PIN", role); snprintf(label, sizeof(label), "%s unblocking PIN (PUK)", role); } hints->dialog_name = name; hints->prompt = prompt; hints->obj_label = label; return get_pin(hints, retstr); } /* * PIN retrieval callback */ static int get_pin_callback(struct sc_profile *profile, int id, const struct sc_pkcs15_auth_info *info, const char *label, u8 *pinbuf, size_t *pinsize) { char namebuf[64]; char *secret = NULL; const char *name = NULL; size_t len = 0; int allocated = 0; if (info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return SC_ERROR_NOT_SUPPORTED; if (label) snprintf(namebuf, sizeof(namebuf), "PIN [%s]", label); else snprintf(namebuf, sizeof(namebuf), "Unspecified PIN [reference %u]", info->attrs.pin.reference); if (!ignore_cmdline_pins) { if (info->auth_method == SC_AC_SYMBOLIC) { switch (id) { case SC_PKCS15INIT_USER_PIN: name = "User PIN"; secret = (char *) opt_pins[OPT_PIN1 & 3]; break; case SC_PKCS15INIT_USER_PUK: name = "User PIN unlock key"; secret = (char *) opt_pins[OPT_PUK1 & 3]; break; case SC_PKCS15INIT_SO_PIN: name = "Security officer PIN"; secret = (char *) opt_pins[OPT_PIN2 & 3]; break; case SC_PKCS15INIT_SO_PUK: name = "Security officer PIN unlock key"; secret = (char *) opt_pins[OPT_PUK2 & 3]; break; } } else if (info->auth_method == SC_AC_CHV) { if (!(info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN) && !(info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN)) { name = "User PIN"; secret = (char *) opt_pins[OPT_PIN1 & 3]; } else if (!(info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN) && (info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN)) { name = "User PUK"; secret = (char *) opt_pins[OPT_PUK1 & 3]; } else if ((info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN) && !(info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN)) { name = "Security officer PIN"; secret = (char *) opt_pins[OPT_PIN2 & 3]; } else if ((info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN) && (info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN)) { name = "Security officer PIN unlock key"; secret = (char *) opt_pins[OPT_PUK2 & 3]; } } if (secret) len = strlen(secret); } if (name && label) snprintf(namebuf, sizeof(namebuf), "%s [%s]", name, label); else if (name) snprintf(namebuf, sizeof(namebuf), "%s", name); name = namebuf; /* See if we were given --secret @ID=.... */ if (!secret) { unsigned int n; for (n = 0; n < opt_secret_count; n++) { struct secret *s = &opt_secrets[n]; if (sc_pkcs15_compare_id(&info->auth_id, &s->id)) { secret = (char *) s->key; len = s->len; break; } } } if (!secret) { sc_ui_hints_t hints; char prompt[128]; int r; if (opt_use_pinpad) return SC_ERROR_OBJECT_NOT_FOUND; snprintf(prompt, sizeof(prompt), "%s required", name); memset(&hints, 0, sizeof(hints)); hints.dialog_name = "pkcs15init.get_pin"; hints.prompt = prompt; hints.obj_label = name; hints.usage = SC_UI_USAGE_OTHER; 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)); return r; } len = strlen(secret); allocated = 1; } if (len > *pinsize) { if (allocated) free(secret); return SC_ERROR_BUFFER_TOO_SMALL; } memcpy(pinbuf, secret, len + 1); *pinsize = len; if (allocated) free(secret); return 0; } static int get_key_callback(struct sc_profile *profile, int method, int reference, const u8 *def_key, size_t def_key_size, u8 *key_buf, size_t *buf_size) { const char *kind, *prompt, *key = NULL; if (def_key_size && opt_use_defkeys) { if (*buf_size < def_key_size) return SC_ERROR_BUFFER_TOO_SMALL; memcpy(key_buf, def_key, def_key_size); *buf_size = def_key_size; return 0; } switch (method) { case SC_AC_PRO: kind = "Secure messaging key"; break; case SC_AC_AUT: kind = "External authentication key"; break; default: /* don't really know what sort of key */ kind = "Key"; break; } printf("Transport key (%s #%d) required.\n", kind, reference); if (opt_use_pinpad) { printf("\n" "Refusing to prompt for transport key because --use-pinpad\n" "was specified on the command line. Please invoke without\n" "--no-prompt, or specify the --use-default-transport-keys\n" "option to use the default transport keys without being\n" "prompted.\n"); fprintf(stderr, "Aborting.\n"); exit(1); } printf("Please enter key in hexadecimal notation " "(e.g. 00:11:22:aa:bb:cc)%s.\n\n", def_key_size? ",\nor press return to accept default" : ""); printf("To use the default transport keys without being prompted,\n" "specify the --use-default-transport-keys option on the\n" "command line (or -T for short), or press Ctrl-C to abort.\n"); while (1) { char buffer[256]; prompt = "Please enter key"; if (def_key_size && def_key_size < 64) { unsigned int j; size_t k = 0; sprintf(buffer, "%s [", prompt); k = strlen(buffer); for (j = 0; j < def_key_size; j++, k += 2) { if (j) buffer[k++] = ':'; sprintf(buffer+k, "%02x", def_key[j]); } buffer[k++] = ']'; buffer[k++] = '\0'; prompt = buffer; } printf("%s: ", prompt); fflush(stdout); #ifdef GET_KEY_ECHO_OFF do { size_t len = 0; int r; /* Read key with echo off - will users really manage? */ r = util_getpass(&key, &len, stdin); if (r < 0 || !key) return SC_ERROR_INTERNAL; } while(0); #else key = fgets(buffer, sizeof(buffer), stdin); if (key) buffer[strcspn(buffer, "\r\n")] = '\0'; #endif if (key == NULL) return SC_ERROR_INTERNAL; if (key[0] == '\0' && def_key_size) { if (*buf_size < def_key_size) return SC_ERROR_BUFFER_TOO_SMALL; memcpy(key_buf, def_key, def_key_size); *buf_size = def_key_size; return 0; } if (sc_hex_to_bin(key, key_buf, buf_size) >= 0) return 0; } } /* * Read a private key */ static int pass_cb(char *buf, int len, int flags, void *d) { size_t pass_len = 0; int plen, r; char *pass = (char *)d; if (!pass) { printf("Please enter passphrase to unlock secret key: "); r = util_getpass(&pass, &pass_len, stdin); if (r < 0 || !pass) return 0; } plen = (int)strlen(pass); if (plen <= 0) return 0; if (plen > len) plen = len; memcpy(buf, pass, plen); return plen; } static int do_read_pem_private_key(const char *filename, const char *passphrase, EVP_PKEY **key) { BIO *bio; bio = BIO_new(BIO_s_file()); if (BIO_read_filename(bio, filename) <= 0) util_fatal("Unable to open %s: %m", filename); *key = PEM_read_bio_PrivateKey(bio, NULL, pass_cb, (char *) passphrase); BIO_free(bio); if (*key == NULL) { sc_log_openssl(g_ctx); return SC_ERROR_CANNOT_LOAD_KEY; } return 0; } static int do_read_pkcs12_private_key(const char *filename, const char *passphrase, EVP_PKEY **key, X509 **certs, unsigned int max_certs) { BIO *bio; PKCS12 *p12; EVP_PKEY *user_key = NULL; X509 *user_cert = NULL; STACK_OF(X509) *cacerts = NULL; int i, ncerts = 0; *key = NULL; bio = BIO_new(BIO_s_file()); if (BIO_read_filename(bio, filename) <= 0) util_fatal("Unable to open %s: %m", filename); p12 = d2i_PKCS12_bio(bio, NULL); BIO_free(bio); if (p12 == NULL || !PKCS12_parse(p12, passphrase, &user_key, &user_cert, &cacerts)) goto error; if (!user_key) { util_error("No key in pkcs12 file?!\n"); return SC_ERROR_CANNOT_LOAD_KEY; } EVP_PKEY_up_ref(user_key); if (user_cert && max_certs) certs[ncerts++] = user_cert; /* Extract CA certificates, if any */ for(i = 0; cacerts && ncerts < (int)max_certs && i < sk_X509_num(cacerts); i++) certs[ncerts++] = sk_X509_value(cacerts, i); /* bump reference counts for certificates */ for (i = 0; i < ncerts; i++) { X509_up_ref(certs[i]); } if (cacerts) sk_X509_free(cacerts); *key = user_key; return ncerts; error: sc_log_openssl(g_ctx); return SC_ERROR_CANNOT_LOAD_KEY; } static int do_read_private_key(const char *filename, const char *format, EVP_PKEY **pk, X509 **certs, unsigned int max_certs) { size_t len = 0; char *passphrase = NULL; int r; if (opt_passphrase) passphrase = (char *) opt_passphrase; if (!format || !strcasecmp(format, "pem")) { r = do_read_pem_private_key(filename, passphrase, pk); } else if (!strcasecmp(format, "pkcs12")) { r = do_read_pkcs12_private_key(filename, passphrase, pk, certs, max_certs); if (r < 0 && !passphrase) { /* this makes only sense for PKCS#12 * PKCS12_parse must support passphrases with * length zero and NULL because of the specification * of PKCS12 - please see the sourcecode of OpenSSL * therefore OpenSSL does not ask for a passphrase like * the PEM interface * see OpenSSL: crypto/pkcs12/p12_kiss.c */ printf("Please enter passphrase to unlock secret key: "); 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); } } else { util_error("Error when reading private key. " "Key file format \"%s\" not supported.\n", format); return SC_ERROR_NOT_SUPPORTED; } if (NULL == opt_passphrase) free(passphrase); if (r < 0) util_fatal("Unable to read private key from %s\n", filename); return r; } /* * Read a public key */ static EVP_PKEY * do_read_pem_public_key(const char *filename) { BIO *bio; EVP_PKEY *pk; bio = BIO_new(BIO_s_file()); if (BIO_read_filename(bio, filename) <= 0) util_fatal("Unable to open %s: %m", filename); pk = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL); BIO_free(bio); if (pk == NULL) sc_log_openssl(g_ctx); return pk; } static EVP_PKEY * do_read_der_public_key(const char *filename) { BIO *bio; EVP_PKEY *pk; bio = BIO_new(BIO_s_file()); if (BIO_read_filename(bio, filename) <= 0) util_fatal("Unable to open %s: %m", filename); pk = d2i_PUBKEY_bio(bio, NULL); BIO_free(bio); if (pk == NULL) sc_log_openssl(g_ctx); return pk; } static int do_read_public_key(const char *name, const char *format, EVP_PKEY **out) { if (!format || !strcasecmp(format, "pem")) { *out = do_read_pem_public_key(name); } else if (!strcasecmp(format, "der")) { *out = do_read_der_public_key(name); } else { util_fatal("Error when reading public key. " "File format \"%s\" not supported.\n", format); } if (!*out) util_fatal("Unable to read public key from %s\n", name); return 0; } /* * Read a certificate */ static X509 * do_read_pem_certificate(const char *filename) { BIO *bio; X509 *xp; bio = BIO_new(BIO_s_file()); if (BIO_read_filename(bio, filename) <= 0) util_fatal("Unable to open %s: %m", filename); xp = PEM_read_bio_X509(bio, NULL, NULL, NULL); BIO_free(bio); if (xp == NULL) sc_log_openssl(g_ctx); return xp; } static X509 * do_read_der_certificate(const char *filename) { BIO *bio; X509 *xp; bio = BIO_new(BIO_s_file()); if (BIO_read_filename(bio, filename) <= 0) util_fatal("Unable to open %s: %m", filename); xp = d2i_X509_bio(bio, NULL); BIO_free(bio); if (xp == NULL) sc_log_openssl(g_ctx); return xp; } static int do_read_certificate(const char *name, const char *format, X509 **out) { if (!format || !strcasecmp(format, "pem")) { *out = do_read_pem_certificate(name); } else if (!strcasecmp(format, "der")) { *out = do_read_der_certificate(name); } else { util_fatal("Error when reading certificate. " "File format \"%s\" not supported.\n", format); } if (!*out) { sc_log_openssl(g_ctx); util_fatal("Unable to read certificate from %s\n", name); } return 0; } static size_t determine_filesize(const char *filename) { FILE *fp; long ll; int rc; fp = fopen(filename,"rb"); if (fp == NULL) util_fatal("Unable to open %s: %m", filename); 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"); fclose(fp); return (size_t)ll; } static int 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); ssize_t sz; *out = malloc(filesize); if (*out == NULL) return SC_ERROR_OUT_OF_MEMORY; inf = fopen(name, "rb"); if (inf == NULL) { fprintf(stderr, "Unable to open '%s' for reading.\n", name); return SC_ERROR_FILE_NOT_FOUND; } sz = fread(*out, 1, filesize, inf); fclose(inf); if (sz < 0) { perror("read"); return SC_ERROR_FILE_NOT_FOUND; } *outlen = filesize; return SC_SUCCESS; } static char * cert_common_name(X509 *x509) { X509_NAME_ENTRY *ne = NULL; 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) { sc_log_openssl(g_ctx); return NULL; } ne = X509_NAME_get_entry(X509_get_subject_name(x509), idx); if (!ne) { sc_log_openssl(g_ctx); return NULL; } a_str = X509_NAME_ENTRY_get_data(ne); if (!a_str) { sc_log_openssl(g_ctx); return NULL; } out_len = ASN1_STRING_to_UTF8(&tmp, a_str); if (!tmp) { sc_log_openssl(g_ctx); return NULL; } out = calloc(1, out_len + 1); if (out) memcpy(out, tmp, out_len); OPENSSL_free(tmp); return out; } static int do_convert_cert(sc_pkcs15_der_t *der, X509 *cert) { u8 *p; der->len = i2d_X509(cert, NULL); der->value = p = malloc(der->len); i2d_X509(cert, &p); return 0; } static unsigned int parse_objects(const char *list, unsigned int action) { unsigned int res = 0; static struct { const char *name; unsigned int flag; } del_flags[] = { {"privkey", SC_PKCS15INIT_TYPE_PRKEY}, {"pubkey", SC_PKCS15INIT_TYPE_PUBKEY}, {"cert", SC_PKCS15INIT_TYPE_CERT}, {"chain", SC_PKCS15INIT_TYPE_CHAIN}, {"data", SC_PKCS15INIT_TYPE_DATA}, {"secrkey", SC_PKCS15INIT_TYPE_SKEY}, {NULL, 0} }; while (1) { int n; size_t len; while (*list == ',') list++; if (!*list) break; len = strcspn(list, ","); if (len == 4 && !strncasecmp(list, "help", 4)) { if (action == ACTION_DELETE_OBJECTS) { printf("\nDelete arguments: a comma-separated list containing any of the following:\n"); printf(" privkey,pubkey,secrkey,cert,chain,data\n"); 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, until there's a CA cert that certifies\n"); printf(" another cert on the card\n"); } else { printf("\nChange attribute argument: either privkey, pubkey, secrkey, cert or data\n"); printf("You also have to specify the --id of the object\n"); printf("For now, you can only change the --label\n"); printf("E.g. pkcs15-init -A cert --id 45 -a 1 --label Jim\n"); } exit(0); } for (n = 0; del_flags[n].name; n++) { if (!strncasecmp(del_flags[n].name, list, len)) { res |= del_flags[n].flag; break; } } if (del_flags[n].name == NULL) { fprintf(stderr, "Unknown argument for --delete-objects: %.*s\n", (int)len, list); exit(0); } list += len; } return res; } /* * Parse X.509 key usage list */ static void parse_x509_usage(const char *list, unsigned int *res) { static struct { const char* name; unsigned int flag; } x509_usage_names[] = { { "digitalSignature", SC_PKCS15INIT_X509_DIGITAL_SIGNATURE }, { "nonRepudiation", SC_PKCS15INIT_X509_NON_REPUDIATION }, { "keyEncipherment", SC_PKCS15INIT_X509_KEY_ENCIPHERMENT }, { "dataEncipherment", SC_PKCS15INIT_X509_DATA_ENCIPHERMENT }, { "keyAgreement", SC_PKCS15INIT_X509_KEY_AGREEMENT }, { "keyCertSign", SC_PKCS15INIT_X509_KEY_CERT_SIGN }, { "cRLSign", SC_PKCS15INIT_X509_CRL_SIGN }, { NULL, 0 } }; static struct { const char * name; const char * list; } x509_usage_aliases[] = { { "sign", "digitalSignature,keyCertSign,cRLSign" }, { "decrypt", "keyEncipherment,dataEncipherment" }, { NULL, NULL } }; while (1) { int n, match = 0; size_t len; while (*list == ',') list++; if (!*list) break; len = strcspn(list, ","); if (len == 4 && !strncasecmp(list, "help", 4)) { printf("Valid X.509 usage names (case-insensitive):\n"); for (n = 0; x509_usage_names[n].name; n++) printf(" %s\n", x509_usage_names[n].name); printf("\nAliases:\n"); for (n = 0; x509_usage_aliases[n].name; n++) { printf(" %-12s %s\n", x509_usage_aliases[n].name, x509_usage_aliases[n].list); } printf("\nUse commas to separate several usage names.\n" "Abbreviated names are okay if unique (e.g. dataEnc)\n"); exit(0); } for (n = 0; x509_usage_names[n].name != NULL; n++) { if (!strncasecmp(x509_usage_names[n].name, list, len)) { *res |= x509_usage_names[n].flag; match++; } } for (n = 0; x509_usage_aliases[n].name; n++) { if (!strncasecmp(x509_usage_aliases[n].name, list, len)) { parse_x509_usage(x509_usage_aliases[n].list, res); match++; } } if (match == 0) { fprintf(stderr, "Unknown X.509 key usage %.*s\n", (int)len, list); exit(1); } if (match > 1) { fprintf(stderr, "Ambiguous X.509 key usage %.*s\n", (int)len, list); exit(1); } list += len; } } /* * Handle one option */ static void handle_option(const struct option *opt) { unsigned int this_action = ACTION_NONE; switch (opt->val) { case 'a': opt_authid = optarg; break; case 'C': this_action = ACTION_INIT; break; case 'E': this_action = ACTION_ERASE; break; case 'G': this_action = ACTION_GENERATE_KEY; opt_newkey = optarg; break; case 'S': this_action = ACTION_STORE_PRIVKEY; opt_infile = optarg; break; case 'P': this_action = ACTION_STORE_PIN; break; case 'X': this_action = ACTION_STORE_CERT; opt_infile = optarg; break; case 'U': this_action = ACTION_UPDATE_CERT; opt_infile = optarg; break; case 'W': this_action = ACTION_STORE_DATA; opt_infile = optarg; break; case 'D': this_action = ACTION_DELETE_OBJECTS; opt_delete_flags = parse_objects(optarg, ACTION_DELETE_OBJECTS); break; case 'A': this_action = ACTION_CHANGE_ATTRIBUTES; opt_type = parse_objects(optarg, ACTION_CHANGE_ATTRIBUTES); break; case 'v': verbose++; break; case 'f': opt_format = optarg; break; case 'h': util_print_usage_and_die(app_name, options, option_help, NULL); /* exit */ case 'i': opt_objectid = optarg; break; case 'l': opt_label = optarg; break; case 'o': opt_outkey = optarg; break; case 'p': opt_profile = optarg; break; case 'c': opt_card_profile = optarg; break; case 'r': opt_reader = optarg; break; case 'u': parse_x509_usage(optarg, &opt_x509_usage); break; case 'w': opt_wait = 1; break; case OPT_PIN1: case OPT_PUK1: case OPT_PIN2: case OPT_PUK2: util_get_pin(optarg, &(opt_pins[opt->val & 3])); break; case OPT_SERIAL: opt_serial = optarg; break; case OPT_PASSPHRASE: util_get_pin(optarg, &opt_passphrase); break; case OPT_PUBKEY: this_action = ACTION_STORE_PUBKEY; opt_infile = optarg; break; case OPT_SECRKEY: this_action = ACTION_STORE_SECRKEY; opt_infile = optarg; break; case OPT_INSECURE: opt_insecure = 1; break; case OPT_EXTRACTABLE: opt_extractable = 1; break; case OPT_AUTHORITY: opt_authority = 1; break; case OPT_APPLICATION_NAME: opt_application_name = optarg; break; case OPT_APPLICATION_ID: opt_application_id = optarg; break; case OPT_BIND_TO_AID: opt_bind_to_aid = optarg; break; case OPT_PUK_ID: opt_puk_authid = optarg; break; case OPT_PUK_LABEL: opt_puk_label = optarg; break; case 'T': opt_use_defkeys = 1; break; case OPT_NO_SOPIN: opt_no_sopin = 1; 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; case OPT_ASSERT_PRISTINE: this_action = ACTION_ASSERT_PRISTINE; break; case OPT_SECRET: parse_secret(&opt_secrets[opt_secret_count], optarg); opt_secret_count++; break; case OPT_SECRKEY_ALGO: opt_secrkey_algo = optarg; break; case OPT_PUBKEY_LABEL: opt_pubkey_label = optarg; break; case 'F': this_action = ACTION_FINALIZE_CARD; break; case OPT_CERT_LABEL: opt_cert_label = optarg; break; case OPT_VERIFY_PIN: opt_verify_pin = 1; break; case OPT_SANITY_CHECK: this_action = ACTION_SANITY_CHECK; break; case OPT_UPDATE_LAST_UPDATE: this_action = ACTION_UPDATE_LAST_UPDATE; break; case OPT_ERASE_APPLICATION: opt_bind_to_aid = optarg; this_action = ACTION_ERASE_APPLICATION; break; case OPT_IGNORE_CA_CERTIFICATES: opt_ignore_ca_certs = 1; break; case OPT_UPDATE_EXISTING: opt_update_existing = 1; break; case OPT_MD_CONTAINER_GUID: opt_md_container_guid = optarg; break; 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); } if ((opt_actions & (1 << this_action)) && opt->has_arg != no_argument) { fprintf(stderr, "Error: you cannot specify option"); if (opt->name) fprintf(stderr, " --%s", opt->name); if (isprint(opt->val)) fprintf(stderr, " -%c", opt->val); fprintf(stderr, " more than once.\n"); util_print_usage_and_die(app_name, options, option_help, NULL); } if (this_action) opt_actions |= (1 << this_action); if ((opt_pins[OPT_PIN2&3] || opt_pins[OPT_PUK2&3]) && opt_no_sopin) { fprintf(stderr, "Error: " "The --no-so-pin option and --so-pin/--so-puk are mutually" "exclusive.\n"); util_print_usage_and_die(app_name, options, option_help, NULL); } if ((opt_actions & (1 << ACTION_ERASE)) && (opt_actions != (1 << ACTION_ERASE))) { fprintf(stderr, "Error: erasing a card is incompatible with all other actions\n"); util_print_usage_and_die(app_name, options, option_help, NULL); } } /* * Parse the command line. */ static void parse_commandline(int argc, char **argv) { const struct option *o; char shortopts[64], *sp; int c, i; /* We make sure the list of short options is always * consistent with the long options */ for (o = options, sp = shortopts; o->name; o++) { if (o->val <= 0 || o->val >= 256) continue; *sp++ = o->val; switch (o->has_arg) { case optional_argument: *sp++ = ':'; /* fall through */ case required_argument: *sp++ = ':'; case no_argument: break; default: util_fatal("Internal: bad has_arg value"); } } sp[0] = 0; while ((c = getopt_long(argc, argv, shortopts, options, &i)) != -1) { /* The optindex seems to be off with some glibc * getopt implementations */ for (o = options; o->name; o++) { if (o->val == c) { handle_option(o); goto next; } } util_fatal("Internal error in options handling, option %u\n", c); next: ; } } /* * Retrieve a PIN from the user. * * @hints dialog hints * @out PIN entered by the user; must be freed. * NULL if dialog was canceled. */ int get_pin(sc_ui_hints_t *hints, char **out) { sc_pkcs15_auth_info_t *pin_info; const char *label; int flags = hints->flags; pin_info = hints->info.pin; if (pin_info && (pin_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN)) return SC_ERROR_NOT_SUPPORTED; if (!(label = hints->obj_label)) { if (pin_info == NULL) label = "PIN"; else if (pin_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN) label = "Security Officer PIN"; else label = "User PIN"; } if (hints->p15card) { /* TBD: get preferredCard from TokenInfo */ } if (hints->prompt) { printf("%s", hints->prompt); if (flags & SC_UI_PIN_OPTIONAL) printf(" (Optional - press return for no PIN)"); printf(".\n"); } *out = NULL; while (1) { char *pin = NULL; size_t len = 0; int r; printf("Please enter %s: ", label); r = util_getpass(&pin, &len, stdin); if (r < 0 || !pin) return SC_ERROR_INTERNAL; if (!strlen(pin) && (flags & SC_UI_PIN_OPTIONAL)) return 0; if (pin_info && (flags & SC_UI_PIN_CHECK_LENGTH)) { if (strlen(pin) < pin_info->attrs.pin.min_length) { fprintf(stderr, "PIN too short (min %lu characters)\n", (unsigned long) pin_info->attrs.pin.min_length); continue; } if (pin_info->attrs.pin.max_length && strlen(pin) > pin_info->attrs.pin.max_length) { fprintf(stderr, "PIN too long (max %lu characters)\n", (unsigned long) pin_info->attrs.pin.max_length); continue; } } *out = strdup(pin); sc_mem_clear(pin, len); if (!(flags & SC_UI_PIN_RETYPE)) break; printf("Please type again to verify: "); r = util_getpass(&pin, &len, stdin); if (r < 0 || !pin) return SC_ERROR_INTERNAL; if (!strcmp(*out, pin)) { sc_mem_clear(pin, len); break; } free(*out); *out = NULL; if (!(flags & SC_UI_PIN_MISMATCH_RETRY)) { fprintf(stderr, "PINs do not match.\n"); free(pin); return SC_ERROR_KEYPAD_PIN_MISMATCH; } fprintf(stderr, "Sorry, the two pins did not match. " "Please try again.\n"); sc_mem_clear(pin, strlen(pin)); /* Currently, there's no way out of this dialog. * We should allow the user to bail out after n * attempts. */ } return 0; } static int verify_pin(struct sc_pkcs15_card *p15card, char *auth_id_str) { struct sc_pkcs15_object *pin_obj = NULL; char pin_label[(sizeof pin_obj->label) + 20]; char *pin = NULL; int r; if (!auth_id_str) { struct sc_pkcs15_object *objs[32]; int ii; r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_AUTH_PIN, objs, 32); if (r < 0) { fprintf(stderr, "PIN code enumeration failed: %s\n", sc_strerror(r)); return -1; } for (ii=0;iidata; if (pin_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) continue; if (pin_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN) continue; if (pin_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN) continue; pin_obj = objs[ii]; break; } } else { struct sc_pkcs15_id auth_id; sc_pkcs15_hex_string_to_id(auth_id_str, &auth_id); r = sc_pkcs15_find_pin_by_auth_id(p15card, &auth_id, &pin_obj); if (r) { fprintf(stderr, "Unable to find PIN code: %s\n", sc_strerror(r)); return r; } } if (!pin_obj) { fprintf(stderr, "PIN object '%s' not found\n", auth_id_str); return -1; } if (opt_pins[0] != NULL) { pin = strdup(opt_pins[0]); } else { sc_ui_hints_t hints; if (opt_use_pinpad) return SC_ERROR_OBJECT_NOT_FOUND; if (pin_obj->label[0]) 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 = 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(pin) : 0); if (r < 0) fprintf(stderr, "Operation failed: %s\n", sc_strerror(r)); if (pin) { sc_mem_clear(pin, strlen(pin)); free(pin); } return r; } OpenSC-0.26.1/src/tools/pkcs15-tool.c000066400000000000000000002016031474147347300171230ustar00rootroot00000000000000/* * pkcs15-tool.c: Tool for poking with PKCS #15 smart cards * * Copyright (C) 2001 Juha Yrjölä * Copyright (C) 2008 Andreas Jellinghaus * * 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" #ifdef __APPLE__ #define _XOPEN_SOURCE 600 #else #define _XOPEN_SOURCE 500 #endif #include #include #ifdef _WIN32 #ifdef __MINGW32__ // work around for https://sourceforge.net/p/mingw-w64/bugs/476/ #include #endif #include #include #else #include #endif #include #ifdef ENABLE_OPENSSL #if defined(HAVE_INTTYPES_H) #include #elif defined(HAVE_STDINT_H) #include #elif defined(_MSC_VER) typedef unsigned __int32 uint32_t; #else #warning no uint32_t type available, please contact opensc-devel@opensc-project.org #endif #include #include #endif #include #include "libopensc/pkcs15.h" #include "libopensc/asn1.h" #include "util.h" #include "pkcs11/pkcs11-display.h" static const char *app_name = "pkcs15-tool"; static int opt_wait = 0; static int opt_no_cache = 0; static int opt_clear_cache = 0; static char * opt_auth_id = NULL; static char * opt_reader = NULL; static char * opt_cert = NULL; static char * opt_data = NULL; static int opt_raw = 0; static char * opt_pubkey = NULL; static char * opt_outfile = NULL; static char * opt_bind_to_aid = NULL; static const char * opt_newpin = NULL; static const char * opt_pin = NULL; static const char * opt_puk = NULL; static int compact = 0; static int verbose = 0; static int opt_use_pinpad = 0; #if defined(ENABLE_OPENSSL) && (defined(_WIN32) || defined(HAVE_INTTYPES_H)) static int opt_rfc4716 = 0; #endif enum { OPT_CHANGE_PIN = 0x100, OPT_LIST_PINS, OPT_READER, OPT_TEST_SESSION_PIN, OPT_PIN_ID, OPT_NO_CACHE, OPT_CLEAR_CACHE, OPT_LIST_PUB, OPT_READ_PUB, #if defined(ENABLE_OPENSSL) && (defined(_WIN32) || defined(HAVE_INTTYPES_H)) OPT_READ_SSH, OPT_RFC4716, #endif OPT_PIN, OPT_NEWPIN, OPT_PUK, OPT_VERIFY_PIN, OPT_BIND_TO_AID, OPT_LIST_APPLICATIONS, OPT_LIST_SKEYS, OPT_USE_PINPAD, OPT_USE_PINPAD_DEPRECATED, OPT_RAW, OPT_PRINT_VERSION, OPT_LIST_INFO, OPT_READ_CERT, }; #define NELEMENTS(x) (sizeof(x)/sizeof((x)[0])) static int authenticate(sc_pkcs15_object_t *obj); 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, OPT_READ_CERT }, { "list-certificates", no_argument, NULL, 'c' }, { "read-data-object", required_argument, NULL, 'R' }, { "raw", no_argument, NULL, OPT_RAW }, { "list-data-objects", no_argument, NULL, 'C' }, { "list-pins", no_argument, NULL, OPT_LIST_PINS }, { "list-secret-keys", no_argument, NULL, OPT_LIST_SKEYS }, { "short", no_argument, NULL, 's' }, { "dump", no_argument, NULL, 'D' }, { "unblock-pin", no_argument, NULL, 'u' }, { "change-pin", no_argument, NULL, OPT_CHANGE_PIN }, { "list-keys", no_argument, NULL, 'k' }, { "list-public-keys", no_argument, NULL, OPT_LIST_PUB }, { "read-public-key", required_argument, NULL, OPT_READ_PUB }, #if defined(ENABLE_OPENSSL) && (defined(_WIN32) || defined(HAVE_INTTYPES_H)) { "read-ssh-key", required_argument, NULL, OPT_READ_SSH }, { "rfc4716", no_argument, NULL, OPT_RFC4716 }, #endif { "test-update", no_argument, NULL, 'T' }, { "update", no_argument, NULL, 'U' }, { "reader", required_argument, NULL, OPT_READER }, { "pin", required_argument, NULL, OPT_PIN }, { "new-pin", required_argument, NULL, OPT_NEWPIN }, { "puk", required_argument, NULL, OPT_PUK }, { "verify-pin", no_argument, NULL, OPT_VERIFY_PIN }, { "test-session-pin", no_argument, NULL, OPT_TEST_SESSION_PIN }, { "output", required_argument, NULL, 'o' }, { "no-cache", no_argument, NULL, OPT_NO_CACHE }, { "clear-cache", no_argument, NULL, OPT_CLEAR_CACHE }, { "auth-id", required_argument, NULL, 'a' }, { "aid", required_argument, NULL, OPT_BIND_TO_AID }, { "wait", no_argument, NULL, 'w' }, { "verbose", no_argument, NULL, 'v' }, { "use-pinpad", no_argument, NULL, OPT_USE_PINPAD }, { "no-prompt", no_argument, NULL, OPT_USE_PINPAD_DEPRECATED }, { NULL, 0, NULL, 0 } }; static const char *option_help[] = { "Print OpenSC package version", "List card information", "List the on-card PKCS#15 applications", "Read certificate with ID ", "List certificates", "Reads data object with OID, applicationName or label ", "Outputs raw 8 bit data to stdout. File output will not be affected by this, it always uses raw mode.", "List data objects", "List PIN codes", "List secret keys", "Output lists in compact format", "List all card objects", "Unblock PIN code", "Change PIN or PUK code", "List private keys", "List public keys", "Reads public key with ID ", #if defined(ENABLE_OPENSSL) && (defined(_WIN32) || defined(HAVE_INTTYPES_H)) "Reads public key with ID , outputs ssh format", "Outputs the public key in RFC 4716 format (requires --read-ssh-key)", #endif "Test if the card needs a security update", "Update the card with a security update", "Uses reader number ", "Specify PIN", "Specify New PIN (when changing or unblocking)", "Specify Unblock PIN", "Verify PIN after card binding (without 'auth-id' the first non-SO, non-Unblock PIN will be verified)", "Equivalent to --verify-pin with additional session PIN generation", "Outputs to file ", "Disable card caching", "Clear card caching", "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, may be used several times", "Do not prompt the user; if no PINs supplied, pinpad will be used.", NULL, NULL }; static sc_context_t *ctx = NULL; static sc_card_t *card = NULL; static struct sc_pkcs15_card *p15card = NULL; struct _access_rule_text { unsigned flag; const char *label; } _access_rules_text[] = { {SC_PKCS15_ACCESS_RULE_MODE_READ, "read"}, {SC_PKCS15_ACCESS_RULE_MODE_UPDATE, "update"}, {SC_PKCS15_ACCESS_RULE_MODE_EXECUTE, "execute"}, {SC_PKCS15_ACCESS_RULE_MODE_DELETE, "delete"}, {SC_PKCS15_ACCESS_RULE_MODE_ATTRIBUTE, "attribute"}, {SC_PKCS15_ACCESS_RULE_MODE_PSO_CDS, "pso_cds"}, {SC_PKCS15_ACCESS_RULE_MODE_PSO_VERIFY, "pso_verify"}, {SC_PKCS15_ACCESS_RULE_MODE_PSO_DECRYPT, "pso_decrypt"}, {SC_PKCS15_ACCESS_RULE_MODE_PSO_ENCRYPT, "pso_encrypt"}, {SC_PKCS15_ACCESS_RULE_MODE_INT_AUTH, "int_auth"}, {SC_PKCS15_ACCESS_RULE_MODE_EXT_AUTH, "ext_auth"}, {0, NULL}, }; static const char *key_types[] = { "", "RSA", "", "GOSTR3410", "EC", "EDDSA", "XEDDSA", "" }; static void print_access_rules(const struct sc_pkcs15_accessrule *rules, int num) { int i, j; if (!rules->access_mode) return; printf("\tAccess Rules :"); for (i = 0; i < num; i++) { int next_coma = 0; if (!(rules + i)->access_mode) break; printf(" "); for (j = 0; _access_rules_text[j].label;j++) { if ((rules + i)->access_mode & (_access_rules_text[j].flag)) { printf("%s%s", next_coma ? "," : "", _access_rules_text[j].label); next_coma = 1; } } printf(":%s;", (rules + i)->auth_id.len ? sc_pkcs15_print_id(&(rules + i)->auth_id) : ""); } printf("\n"); } static void print_common_flags(const struct sc_pkcs15_object *obj) { const char *common_flags[] = {"private", "modifiable"}; unsigned int i; 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]); } } printf("\n"); } 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), sc_pkcs15_print_id(&cert_info->id)); if (cert_info->authority) printf(" Authority"); return; } printf("X.509 Certificate [%.*s]\n", (int) sizeof obj->label, obj->label); print_common_flags(obj); printf("\tAuthority : %s\n", cert_info->authority ? "yes" : "no"); printf("\tPath : %s\n", sc_print_path(&cert_info->path)); printf("\tID : %s\n", sc_pkcs15_print_id(&cert_info->id)); print_access_rules(obj->access_rules, SC_PKCS15_MAX_ACCESS_RULES); 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, ""); printf("\n"); sc_pkcs15_free_certificate(cert_parsed); } } static int list_certificates(void) { int r, i; struct sc_pkcs15_object *objs[32]; r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_CERT_X509, objs, 32); if (r < 0) { fprintf(stderr, "Certificate enumeration failed: %s\n", sc_strerror(r)); return 1; } if (compact) printf("Card has %d Certificate(s).\n", r); else if (verbose) printf("Card has %d Certificate(s).\n\n", r); for (i = 0; i < r; i++) { print_cert_info(objs[i]); printf("\n"); } return 0; } static int print_pem_object(const char *kind, const u8*data, size_t data_len) { FILE *outf; unsigned char *buf = NULL; size_t buf_len = 1024; int r; /* With base64, every 3 bytes yield 4 characters, and with * 64 chars per line we know almost exactly how large a buffer we * will need. */ buf_len = (data_len + 2) / 3 * 4; buf_len += 2 * (buf_len / 64 + 2); /* certain platforms use CRLF */ buf_len += 64; /* slack for checksum etc */ if (!(buf = malloc(buf_len))) { perror("print_pem_object"); return 1; } r = sc_base64_encode(data, data_len, buf, buf_len, 64); if (r < 0) { fprintf(stderr, "Base64 encoding failed: %s\n", sc_strerror(r)); free(buf); return 1; } if (opt_outfile != NULL) { outf = fopen(opt_outfile, "w"); if (outf == NULL) { fprintf(stderr, "Error opening file '%s': %s\n", opt_outfile, strerror(errno)); free(buf); return 2; } } else outf = stdout; fprintf(outf, "-----BEGIN %s-----\n" "%s" "-----END %s-----\n", kind, buf, kind); if (outf != stdout) fclose(outf); free(buf); return 0; } static void list_data_object(const char *kind, const unsigned char *data, size_t data_len) { char title[0x100]; size_t i; snprintf(title, sizeof(title), "%s (%lu bytes): ", kind, (unsigned long) data_len); printf("%s", title); memset(title, ' ', strlen(title)); for (i = 0; i < data_len; i++) { if (i && !(i%48)) printf("\n%s", title); printf("%02X", data[i]); } printf("\n"); } static int print_data_object(const char *kind, const u8*data, size_t data_len) { size_t i; if (opt_outfile != NULL) { FILE *outf; outf = fopen(opt_outfile, "wb"); if (outf == NULL) { fprintf(stderr, "Error opening file '%s': %s\n", opt_outfile, strerror(errno)); return 2; } for (i=0; i < data_len; i++) fprintf(outf, "%c", data[i]); fclose(outf); } else { if (opt_raw) { for (i=0; i < data_len; i++) printf("%c", data[i]); } else { printf("%s (%lu bytes): <", kind, (unsigned long) data_len); for (i=0; i < data_len; i++) printf(" %02X", data[i]); printf(" >\n"); } } return 0; } static int read_certificate(void) { int r, i, count; struct sc_pkcs15_id id; struct sc_pkcs15_object *objs[32]; id.len = SC_PKCS15_MAX_ID_SIZE; sc_pkcs15_hex_string_to_id(opt_cert, &id); r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_CERT_X509, objs, 32); if (r < 0) { fprintf(stderr, "Certificate enumeration failed: %s\n", sc_strerror(r)); return 1; } count = r; 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); 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; } r = print_pem_object("CERTIFICATE", cert->data.value, cert->data.len); sc_pkcs15_free_certificate(cert); return r; } fprintf(stderr, "Certificate with ID '%s' not found.\n", opt_cert); return 2; } static int read_data_object(void) { int r, i, count; struct sc_pkcs15_object *objs[32]; struct sc_object_id oid; r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_DATA_OBJECT, objs, 32); if (r < 0) { fprintf(stderr, "Data object enumeration failed: %s\n", sc_strerror(r)); return 1; } count = r; 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)) continue; } else { if (strcmp(opt_data, cinfo->app_label) && strncmp(opt_data, objs[i]->label, sizeof objs[i]->label)) continue; } if (verbose) printf("Reading data object with label '%s'\n", opt_data); r = authenticate(objs[i]); if (r >= 0) { 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) continue; /* DEE emulation may say there is a file */ return 1; } r = print_data_object("Data Object", data_object->data, data_object->data_len); sc_pkcs15_free_data_object(data_object); return r; } else { fprintf(stderr, "Authentication error: %s\n", sc_strerror(r)); return 1; } } fprintf(stderr, "Data object with label '%s' not found.\n", opt_data); return 2; } static int list_data_objects(void) { int r, i, count; struct sc_pkcs15_object *objs[32]; r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_DATA_OBJECT, objs, 32); if (r < 0) { fprintf(stderr, "Data object enumeration failed: %s\n", sc_strerror(r)); return 1; } count = r; if (compact) printf("Card has %d Data object(s).\n", count); else if (verbose) printf("Card has %d Data object(s).\n\n", count); for (i = 0; i < count; i++) { int idx; struct sc_pkcs15_data_info *cinfo = (struct sc_pkcs15_data_info *) objs[i]->data; if (compact) { printf("\tPath:%-12s", sc_print_path(&cinfo->path)); if (sc_valid_oid(&cinfo->app_oid)) { printf(" %i", cinfo->app_oid.value[0]); for (idx = 1; idx < SC_MAX_OBJECT_ID_OCTETS && cinfo->app_oid.value[idx] != -1 ; idx++) printf(".%i", cinfo->app_oid.value[idx]); } if (objs[i]->auth_id.len == 0) { struct sc_pkcs15_data *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) continue; /* DEE emulation may say there is a file */ return 1; } sc_pkcs15_free_data_object(data_object); printf(" Size:%5"SC_FORMAT_LEN_SIZE_T"u", cinfo->data.len); } else { printf(" AuthID:%-3s", sc_pkcs15_print_id(&objs[i]->auth_id)); } printf(" %-20s", cinfo->app_label); printf("\n"); continue; } if (objs[i]->label[0] != '\0') printf("Data object '%.*s'\n",(int) sizeof objs[i]->label, objs[i]->label); else printf("Data object <%i>\n", i); printf("\tapplicationName: %s\n", cinfo->app_label); if (sc_valid_oid(&cinfo->app_oid)) { printf("\tapplicationOID: %i", cinfo->app_oid.value[0]); for (idx = 1; idx < SC_MAX_OBJECT_ID_OCTETS && cinfo->app_oid.value[idx] != -1 ; idx++) printf(".%i", cinfo->app_oid.value[idx]); printf("\n"); } printf("\tPath: %s\n", sc_print_path(&cinfo->path)); if (objs[i]->auth_id.len == 0) { struct sc_pkcs15_data *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) continue; /* DEE emulation may say there is a file */ return 1; } list_data_object("\tData", data_object->data, data_object->data_len); sc_pkcs15_free_data_object(data_object); } else { printf("\tAuth ID: %s\n", sc_pkcs15_print_id(&objs[i]->auth_id)); } printf("\n"); } return 0; } static void print_key_usages(unsigned int usage) { size_t i; const char *usages[] = { "encrypt", "decrypt", "sign", "signRecover", "wrap", "unwrap", "verify", "verifyRecover", "derive", "nonRepudiation" }; const size_t usage_count = NELEMENTS(usages); for (i = 0; i < usage_count; i++) if (usage & (1 << i)) printf(", %s", usages[i]); } static void print_key_access_flags(int flags) { size_t i; const char *key_access_flags[] = { "sensitive", "extract", "alwaysSensitive","neverExtract", "local" }; const size_t af_count = NELEMENTS(key_access_flags); for (i = 0; i < af_count; i++) if (flags & (1 << i)) printf(", %s", key_access_flags[i]); } 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]); if (prkey->modulus_length) printf("[%lu]", (unsigned long)prkey->modulus_length); else { if (prkey->field_length) printf("[%lu]", (unsigned long)prkey->field_length); } printf(" ID:%s", sc_pkcs15_print_id(&prkey->id)); 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 %-18.*s [0x%02X", (int)sizeof obj->label, obj->label, prkey->usage); print_key_usages(prkey->usage); printf("]"); return; } printf("Private %s Key [%.*s]\n", key_types[7 & obj->type], (int) sizeof obj->label, obj->label); print_common_flags(obj); printf("\tUsage : [0x%02X]", prkey->usage); print_key_usages(prkey->usage); printf("\n"); 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); if (prkey->modulus_length) printf("\tModLength : %lu\n", (unsigned long)prkey->modulus_length); else printf("\tFieldLength : %lu\n", (unsigned long)prkey->field_length); 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)); if (obj->auth_id.len != 0) printf("\tAuth ID : %s\n", sc_pkcs15_print_id(&obj->auth_id)); printf("\tID : %s\n", sc_pkcs15_print_id(&prkey->id)); guid_len = sizeof(guid); if (!sc_pkcs15_get_object_guid(p15card, obj, 1, guid, &guid_len)) { printf("\tMD:guid : "); if (strlen((char *)guid) == guid_len) { printf("%s\n", (char *)guid); } else { printf("0x'"); util_hex_dump(stdout, guid, guid_len, ""); printf("'\n"); } } } static int list_private_keys(void) { int r, i; struct sc_pkcs15_object *objs[32]; r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_PRKEY, objs, 32); if (r < 0) { fprintf(stderr, "Private key enumeration failed: %s\n", sc_strerror(r)); return 1; } if (compact) printf("Card has %d Private key(s).\n", r); else if (verbose) printf("Card has %d Private key(s).\n\n", r); for (i = 0; i < r; i++) { print_prkey_info(objs[i]); printf("\n"); } return 0; } static void print_pubkey_info(const struct sc_pkcs15_object *obj) { const struct sc_pkcs15_pubkey_info *pubkey = (const struct sc_pkcs15_pubkey_info *) obj->data; int have_path = (pubkey->path.len != 0) || (pubkey->path.aid.len != 0); if (compact) { printf("\t%-3s", key_types[7 & obj->type]); if (pubkey->modulus_length) printf("[%lu]", (unsigned long)pubkey->modulus_length); else printf("[FieldLength:%lu]", (unsigned long)pubkey->field_length); printf(" %s", sc_pkcs15_print_id(&pubkey->id)); printf(" Ref:0x%02X", pubkey->key_reference); if (obj->auth_id.len != 0) printf(" AuthID:%s", sc_pkcs15_print_id(&obj->auth_id)); printf(" %-18.*s [0x%02X", (int)sizeof obj->label, obj->label, pubkey->usage); print_key_usages(pubkey->usage); printf("]"); return; } printf("Public %s Key [%.*s]\n", key_types[7 & obj->type], (int) sizeof obj->label, obj->label); print_common_flags(obj); printf("\tUsage : [0x%02X]", pubkey->usage); print_key_usages(pubkey->usage); printf("\n"); printf("\tAccess Flags : [0x%02X]", pubkey->access_flags); print_key_access_flags(pubkey->access_flags); printf("\n"); print_access_rules(obj->access_rules, SC_PKCS15_MAX_ACCESS_RULES); if (pubkey->modulus_length) { printf("\tModLength : %lu\n", (unsigned long)pubkey->modulus_length); } else if (pubkey->field_length) { printf("\tFieldLength : %lu\n", (unsigned long)pubkey->field_length); } 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); sc_pkcs15_free_pubkey(pkey); } } 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)); if (obj->auth_id.len != 0) printf("\tAuth ID : %s\n", sc_pkcs15_print_id(&obj->auth_id)); printf("\tID : %s\n", sc_pkcs15_print_id(&pubkey->id)); if (!have_path || obj->content.len) printf("\tDirectValue : <%s>\n", obj->content.len ? "present" : "absent"); } static int list_public_keys(void) { int r, i; struct sc_pkcs15_object *objs[32]; r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_PUBKEY, objs, 32); if (r < 0) { fprintf(stderr, "Public key enumeration failed: %s\n", sc_strerror(r)); return 1; } if (compact) printf("Card has %d Public key(s).\n", r); else if (verbose) printf("Card has %d Public key(s).\n\n", r); for (i = 0; i < r; i++) { print_pubkey_info(objs[i]); printf("\n"); } return 0; } static int read_public_key(void) { int r; struct sc_pkcs15_id id; struct sc_pkcs15_object *obj; sc_pkcs15_pubkey_t *pubkey = NULL; sc_pkcs15_cert_t *cert = NULL; sc_pkcs15_der_t pem_key; pem_key.value = NULL; pem_key.len = 0; id.len = SC_PKCS15_MAX_ID_SIZE; sc_pkcs15_hex_string_to_id(opt_pubkey, &id); r = sc_pkcs15_find_pubkey_by_id(p15card, &id, &obj); if (r >= 0) { if (verbose) printf("Reading public key with ID '%s'\n", opt_pubkey); 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); 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) { fprintf(stderr, "Public key with ID '%s' not found.\n", opt_pubkey); r = 2; goto out; } if (r < 0) { fprintf(stderr, "Public key enumeration failed: %s\n", sc_strerror(r)); r = 1; goto out; } if (!pubkey) { fprintf(stderr, "Public key not available\n"); r = 1; goto out; } r = sc_pkcs15_encode_pubkey_as_spki(ctx, pubkey, &pem_key.value, &pem_key.len); if (r < 0) { fprintf(stderr, "Error encoding PEM key: %s\n", sc_strerror(r)); r = 1; } else { r = print_pem_object("PUBLIC KEY", pem_key.value, pem_key.len); free(pem_key.value); } out: if (cert) sc_pkcs15_free_certificate(cert); else sc_pkcs15_free_pubkey(pubkey); return r; } static void print_skey_info(const struct sc_pkcs15_object *obj) { static const char *skey_types[] = { "", "Generic", "DES", "2DES", "3DES", "", "", "" }; struct sc_pkcs15_skey_info *skey = (struct sc_pkcs15_skey_info *) obj->data; unsigned char guid[40]; size_t guid_len; printf("Secret %s Key [%.*s]\n", skey_types[7 & obj->type], (int) sizeof obj->label, obj->label); print_common_flags(obj); printf("\tUsage : [0x%02X]", skey->usage); print_key_usages(skey->usage); printf("\n"); printf("\tAccess Flags : [0x%02X]", skey->access_flags); print_key_access_flags(skey->access_flags); printf("\n"); print_access_rules(obj->access_rules, SC_PKCS15_MAX_ACCESS_RULES); 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%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)); guid_len = sizeof(guid); if (!sc_pkcs15_get_object_guid(p15card, obj, 1, guid, &guid_len)) { printf("\tGUID : %s\n", (char *)guid); } } static int list_skeys(void) { int r, i; struct sc_pkcs15_object *objs[32]; r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_SKEY, objs, 32); if (r < 0) { fprintf(stderr, "Secret key enumeration failed: %s\n", sc_strerror(r)); return 1; } if (verbose) printf("Card has %d Secret key(s).\n\n", r); for (i = 0; i < r; i++) { print_skey_info(objs[i]); printf("\n"); } return 0; } #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, size_t len) { unsigned char *uu; int r; uu = malloc(len*2); // Way over - even if we have extra LFs; as each 6 bits take one byte. if (!uu) return; if (opt_rfc4716) { r = sc_base64_encode(buf, len, uu, 2*len, 64); if (r < 0) { free(uu); return; } fprintf(outf,"---- BEGIN SSH2 PUBLIC KEY ----\n"); if (obj->label[0] != '\0') fprintf(outf,"Comment: \"%.*s\"\n", (int) sizeof obj->label, obj->label); fprintf(outf,"%s", uu); fprintf(outf,"---- END SSH2 PUBLIC KEY ----\n"); } else { // Old style openssh - [ [ anything else] // r = sc_base64_encode(buf, len, uu, 2*len, 0); if (r < 0) { free(uu); return; } if (obj->label[0] != '\0') fprintf(outf,"%s %s %.*s\n", alg, uu, (int) sizeof obj->label, obj->label); else fprintf(outf,"%s %s\n", alg, uu); } free(uu); return; } static int read_ssh_key(void) { int r; struct sc_pkcs15_id id; struct sc_pkcs15_object *obj = NULL; sc_pkcs15_pubkey_t *pubkey = NULL; sc_pkcs15_cert_t *cert = NULL; FILE *outf = NULL; if (opt_outfile != NULL) { outf = fopen(opt_outfile, "w"); if (outf == NULL) { fprintf(stderr, "Error opening file '%s': %s\n", opt_outfile, strerror(errno)); goto fail2; } } else { outf = stdout; } id.len = SC_PKCS15_MAX_ID_SIZE; sc_pkcs15_hex_string_to_id(opt_pubkey, &id); r = sc_pkcs15_find_pubkey_by_id(p15card, &id, &obj); if (r >= 0) { if (verbose) fprintf(stderr,"Reading ssh key with ID '%s'\n", opt_pubkey); r = authenticate(obj); if (r >= 0) 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) fprintf(stderr,"Reading certificate with ID '%s'\n", opt_pubkey); 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 (opt_outfile != NULL) fclose(outf); fprintf(stderr, "Public key with ID '%s' not found.\n", opt_pubkey); return 2; } if (r < 0) { 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.eddsa.pubkey.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.eddsa.pubkey.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]; 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) { fprintf(stderr, "Failed to decode public RSA key.\n"); goto fail2; } buf[0]=0; buf[1]=0; buf[2]=0; buf[3]=7; len = sprintf((char *) buf+4,"ssh-rsa"); len+=4; if (sizeof(buf)-len < 4+pubkey->u.rsa.exponent.len) goto fail; n = pubkey->u.rsa.exponent.len; if (pubkey->u.rsa.exponent.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.rsa.exponent.data[0] & 0x80) buf[len++]= 0; memcpy(buf+len,pubkey->u.rsa.exponent.data, pubkey->u.rsa.exponent.len); len += pubkey->u.rsa.exponent.len; if (sizeof(buf)-len < 5+pubkey->u.rsa.modulus.len) goto fail; n = pubkey->u.rsa.modulus.len; if (pubkey->u.rsa.modulus.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.rsa.modulus.data[0] & 0x80) buf[len++]= 0; memcpy(buf+len,pubkey->u.rsa.modulus.data, pubkey->u.rsa.modulus.len); len += pubkey->u.rsa.modulus.len; print_ssh_key(outf, "ssh-rsa", obj, buf, len); } if (opt_outfile != NULL) fclose(outf); if (cert) sc_pkcs15_free_certificate(cert); else sc_pkcs15_free_pubkey(pubkey); return 0; fail: printf("can't convert key: buffer too small\n"); fail2: if (opt_outfile != NULL && outf != NULL) fclose(outf); if (cert) sc_pkcs15_free_certificate(cert); else sc_pkcs15_free_pubkey(pubkey); return SC_ERROR_OUT_OF_MEMORY; } #endif static sc_pkcs15_object_t * get_pin_info(void) { sc_pkcs15_object_t *objs[32], *obj; int r; if (opt_auth_id == NULL) { r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_AUTH_PIN, objs, 32); if (r < 0) { fprintf(stderr, "PIN code enumeration failed: %s\n", sc_strerror(r)); return NULL; } if (r == 0) { fprintf(stderr, "No PIN codes found.\n"); return NULL; } obj = objs[0]; } else { struct sc_pkcs15_id auth_id; sc_pkcs15_hex_string_to_id(opt_auth_id, &auth_id); r = sc_pkcs15_find_pin_by_auth_id(p15card, &auth_id, &obj); if (r) { fprintf(stderr, "Unable to find PIN code: %s\n", sc_strerror(r)); return NULL; } } return obj; } static u8 * get_pin(const char *prompt, sc_pkcs15_object_t *pin_obj) { sc_pkcs15_auth_info_t *pinfo = (sc_pkcs15_auth_info_t *) pin_obj->data; char *pincode = NULL; size_t len = 0; int r; if (opt_use_pinpad) { // defer entry of the PIN to the readers pinpad. if (verbose) printf("%s [%.*s]: entry deferred to the reader keypad\n", prompt, (int) sizeof pin_obj->label, pin_obj->label); return NULL; } printf("%s [%.*s]: ", prompt, (int) sizeof pin_obj->label, pin_obj->label); if (pinfo->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return NULL; while (1) { r = util_getpass(&pincode, &len, stdin); if (r < 0) return NULL; if (!pincode || strlen(pincode) == 0) { free(pincode); return NULL; } if (strlen(pincode) < pinfo->attrs.pin.min_length) { printf("PIN code too short, try again.\n"); continue; } if (strlen(pincode) > pinfo->attrs.pin.max_length) { printf("PIN code too long, try again.\n"); continue; } return (u8 *) pincode; } } #ifdef _WIN32 static int clear_cache(void) { TCHAR dirname[PATH_MAX]; SHFILEOPSTRUCT fileop; int r; fileop.hwnd = NULL; // no status display fileop.wFunc = FO_DELETE; // delete operation fileop.pFrom = dirname; // source file name as double null terminated string fileop.pTo = NULL; // no destination needed fileop.fFlags = FOF_NOCONFIRMATION|FOF_SILENT; // do not prompt the user fileop.fAnyOperationsAborted = FALSE; fileop.lpszProgressTitle = NULL; fileop.hNameMappings = NULL; /* remove the user's cache directory */ if ((r = sc_get_cache_dir(ctx, dirname, sizeof(dirname))) < 0) return r; dirname[_tcslen(dirname)+1] = 0; printf("Deleting %s...", dirname); r = SHFileOperation(&fileop); if (r == 0) { printf(" OK\n"); } else { printf(" Error\n"); } _tcscpy(dirname, _T("C:\\Windows\\System32\\config\\systemprofile\\eid-cache")); dirname[_tcslen(dirname)+1] = 0; printf("Deleting %s...", dirname); r = SHFileOperation(&fileop); if (r == 0) { printf(" OK\n"); } else { printf(" Error\n"); } return r; } #else static int unlink_cb(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf) { int r = remove(fpath); if (r) perror(fpath); return r; } static int clear_cache(void) { char dirname[PATH_MAX]; int r = 0; /* remove the user's cache directory */ if ((r = sc_get_cache_dir(ctx, dirname, sizeof(dirname))) < 0) return r; r = nftw(dirname, unlink_cb, 64, FTW_DEPTH | FTW_PHYS); return r; } #endif static int verify_pin(void) { struct sc_pkcs15_object *pin_obj = NULL; unsigned char *pin; int r; if (!opt_auth_id) { struct sc_pkcs15_object *objs[32]; int ii; r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_AUTH_PIN, objs, 32); if (r < 0) { fprintf(stderr, "PIN code enumeration failed: %s\n", sc_strerror(r)); return -1; } for (ii=0;iidata; if (pin_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) continue; if (pin_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN) continue; if (pin_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN) continue; pin_obj = objs[ii]; break; } } else { pin_obj = get_pin_info(); } if (!pin_obj) { fprintf(stderr, "PIN object '%s' not found\n", opt_auth_id); return -1; } if (opt_pin != NULL) pin = (unsigned char *) opt_pin; else pin = get_pin("Please enter PIN", pin_obj); r = sc_pkcs15_verify_pin(p15card, pin_obj, pin, pin ? strlen((char *) pin) : 0); if (opt_pin == NULL) free(pin); if (r < 0) { fprintf(stderr, "Operation failed: %s\n", sc_strerror(r)); return -1; } return 0; } static int test_session_pin(void) { struct sc_pkcs15_object *pin_obj = NULL; struct sc_pkcs15_auth_info *auth_info = NULL; unsigned int auth_method; unsigned char *pin; int r; unsigned char sessionpin[SC_MAX_PIN_SIZE] = {0}; size_t sessionpinlen = sizeof sessionpin; if (!opt_auth_id) { struct sc_pkcs15_object *objs[32]; int ii; r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_AUTH_PIN, objs, 32); if (r < 0) { fprintf(stderr, "PIN code enumeration failed: %s\n", sc_strerror(r)); return -1; } for (ii=0;iidata; if (pin_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) continue; if (pin_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN) continue; if (pin_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN) continue; pin_obj = objs[ii]; break; } } else { pin_obj = get_pin_info(); } if (!(card->caps & SC_CARD_CAP_SESSION_PIN)) { fprintf(stderr, "Card does not support session PIN. Will try anyway.\n"); } if (!pin_obj) { fprintf(stderr, "PIN object '%s' not found\n", opt_auth_id); return -1; } if (opt_pin != NULL) pin = (unsigned char *) opt_pin; else pin = get_pin("Please enter PIN", pin_obj); r = sc_pkcs15_verify_pin_with_session_pin(p15card, pin_obj, pin, pin ? strlen((char *) pin) : 0, sessionpin, &sessionpinlen); if (opt_pin == NULL) free(pin); if (r < 0) { fprintf(stderr, "Operation failed: %s\n", sc_strerror(r)); return -1; } if (!sessionpinlen) { fprintf(stderr, "Could not generate session PIN\n"); return -1; } printf("Generated session PIN (in hexadecimal form): "); util_hex_dump(stdout, sessionpin, sessionpinlen, ""); puts(""); auth_info = (struct sc_pkcs15_auth_info *)pin_obj->data; /* save the pin type */ auth_method = auth_info->auth_method; auth_info->auth_method = SC_AC_SESSION; r = sc_pkcs15_verify_pin(p15card, pin_obj, sessionpin, sessionpinlen); /* restore the pin type */ auth_info->auth_method = auth_method; if (r < 0) { fprintf(stderr, "Could not verify session PIN: %s\n", sc_strerror(r)); return -1; } puts("Verified session PIN"); return 0; } static int authenticate(sc_pkcs15_object_t *obj) { sc_pkcs15_object_t *pin_obj; u8 *pin = NULL; int r; if (obj->auth_id.len == 0) return 0; r = sc_pkcs15_find_pin_by_auth_id(p15card, &obj->auth_id, &pin_obj); if (r) return r; if (opt_pin != NULL) pin = (u8 *) opt_pin; else pin = get_pin("Please enter PIN", pin_obj); r = sc_pkcs15_verify_pin(p15card, pin_obj, pin, pin? strlen((char *) pin) : 0); if (opt_pin == NULL) free(pin); return r; } static void print_pin_info(const struct sc_pkcs15_object *obj) { const char *pin_flags[] = { "case-sensitive", "local", "change-disabled", "unblock-disabled", "initialized", "needs-padding", "unblockingPin", "soPin", "disable_allowed", "integrity-protected", "confidentiality-protected", "exchangeRefData" }; const char *pin_types[] = {"bcd", "ascii-numeric", "UTF-8", "halfnibble bcd", "iso 9664-1"}; const struct sc_pkcs15_auth_info *auth_info = (const struct sc_pkcs15_auth_info *) obj->data; 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 (compact) { printf("\t%-3s ID:%s", obj->type == SC_PKCS15_TYPE_AUTH_PIN ? "PIN" : "Key", sc_pkcs15_print_id(&auth_info->auth_id)); if (auth_info->auth_type == SC_PKCS15_PIN_AUTH_TYPE_PIN) { const struct sc_pkcs15_pin_attributes *pin_attrs = &(auth_info->attrs.pin); printf(" Ref:0x%02X", pin_attrs->reference); } else { const struct sc_pkcs15_authkey_attributes *attrs = &auth_info->attrs.authkey; printf(" Derived:%i", attrs->derived); printf(" SecretKeyID:%s", sc_pkcs15_print_id(&attrs->skey_id)); } if (obj->auth_id.len) printf(" AuthID:%s", sc_pkcs15_print_id(&obj->auth_id)); if (auth_info->path.len || auth_info->path.aid.len) printf(" Path:%s", sc_print_path(&auth_info->path)); if (auth_info->tries_left >= 0) printf(" Tries:%d", auth_info->tries_left); printf(" %.*s", (int) sizeof obj->label, obj->label); return; } printf("%s [%.*s]\n", obj->type == SC_PKCS15_TYPE_AUTH_PIN ? "PIN" : "AuthKey", (int) sizeof obj->label, obj->label); print_common_flags(obj); if (obj->auth_id.len) printf("\tAuth ID : %s\n", sc_pkcs15_print_id(&obj->auth_id)); printf("\tID : %s\n", sc_pkcs15_print_id(&auth_info->auth_id)); if (auth_info->auth_type == SC_PKCS15_PIN_AUTH_TYPE_PIN) { const struct sc_pkcs15_pin_attributes *pin_attrs = &(auth_info->attrs.pin); printf("\tFlags : [0x%02X]", pin_attrs->flags); for (i = 0; i < pf_count; i++) if (pin_attrs->flags & (1 << i)) printf(", %s", pin_flags[i]); printf("\n"); printf("\tLength : min_len:%lu, max_len:%lu, stored_len:%lu\n", (unsigned long)pin_attrs->min_length, (unsigned long)pin_attrs->max_length, (unsigned long)pin_attrs->stored_length); printf("\tPad char : 0x%02X\n", pin_attrs->pad_char); printf("\tReference : %d (0x%02X)\n", pin_attrs->reference, pin_attrs->reference); if (pin_attrs->type < NELEMENTS(pin_types)) printf("\tType : %s\n", pin_types[pin_attrs->type]); else printf("\tType : [encoding %d]\n", pin_attrs->type); } else if (auth_info->auth_type == SC_PKCS15_PIN_AUTH_TYPE_AUTH_KEY) { const struct sc_pkcs15_authkey_attributes *attrs = &auth_info->attrs.authkey; printf("\tDerived : %i\n", attrs->derived); printf("\tSecretKeyID : %s\n", sc_pkcs15_print_id(&attrs->skey_id)); } if (auth_info->path.len || auth_info->path.aid.len) printf("\tPath : %s\n", sc_print_path(&auth_info->path)); if (auth_info->tries_left >= 0) printf("\tTries left : %d\n", auth_info->tries_left); } static int list_pins(void) { int r, i; struct sc_pkcs15_object *objs[32]; r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_AUTH, objs, 32); if (r < 0) { fprintf(stderr, "AUTH objects enumeration failed: %s\n", sc_strerror(r)); return 1; } if (compact) printf("Card has %d Authentication object(s).\n", r); else if (verbose) 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"); } return 0; } static int list_apps(FILE *fout) { unsigned j; int i; for (i=0; icard->app_count; i++) { struct sc_app_info *info = p15card->card->app[i]; fprintf(fout, "Application '%s':\n", info->label); fprintf(fout, "\tAID: "); for(j=0;jaid.len;j++) fprintf(fout, "%02X", info->aid.value[j]); fprintf(fout, "\n"); if (info->ddo.value && info->ddo.len) { fprintf(fout, "\tDDO: "); for(j=0;jddo.len;j++) fprintf(fout, "%02X", info->ddo.value[j]); fprintf(fout, "\n"); } fprintf(fout, "\n"); } 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[] = { "Read-only", "Login required", "PRN generation", "EID compliant" }; 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); printf("\tSerial number : %s\n", p15card->tokeninfo->serial_number); printf("\tManufacturer ID: %s\n", p15card->tokeninfo->manufacturer_id); if (last_update) printf("\tLast update : %s\n", last_update); if (p15card->tokeninfo->preferred_language) printf("\tLanguage : %s\n", p15card->tokeninfo->preferred_language); if (p15card->tokeninfo->profile_indication.name) printf("\tProfile : %s\n", p15card->tokeninfo->profile_indication.name); printf("\tFlags : "); for (i = 0; i < 4; i++) { if ((p15card->tokeninfo->flags >> i) & 1) { if (count) printf(", "); printf("%s", flags[i]); count++; } } 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) { list_info(); list_pins(); list_private_keys(); list_public_keys(); list_skeys(); list_certificates(); list_data_objects(); return 0; } static int unblock_pin(void) { struct sc_pkcs15_auth_info *pinfo = NULL; sc_pkcs15_object_t *pin_obj; u8 *pin, *puk; int r, pinpad_present = 0; pinpad_present = p15card->card->reader->capabilities & SC_READER_CAP_PIN_PAD || p15card->card->caps & SC_CARD_CAP_PROTECTED_AUTHENTICATION_PATH; if (!(pin_obj = get_pin_info())) return 2; pinfo = (sc_pkcs15_auth_info_t *) pin_obj->data; if (pinfo->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return 1; puk = (u8 *) opt_puk; if (puk == NULL) { sc_pkcs15_object_t *puk_obj = NULL; if (pin_obj->auth_id.len) { r = sc_pkcs15_find_pin_by_auth_id(p15card, &pin_obj->auth_id, &puk_obj); if (r < 0) { fprintf(stderr, "Failed to find PUK object for PIN: %s\n", sc_strerror(r)); return 2; } } if (puk_obj) { struct sc_pkcs15_auth_info *puk_info = (sc_pkcs15_auth_info_t *) puk_obj->data; if (puk_info->auth_type == SC_PKCS15_PIN_AUTH_TYPE_PIN) { /* TODO: Print PUK's label */ puk = get_pin("Enter PUK", puk_obj); if (!pinpad_present && puk == NULL) return 2; } } else { puk = get_pin("Enter PUK", pin_obj); if (!pinpad_present && puk == NULL) return 2; } } if (puk == NULL && verbose) printf("PUK value will be prompted with pinpad.\n"); /* FIXME should OPENSSL_cleanse on pin/puk data */ pin = opt_pin ? (u8 *) opt_pin : (u8 *) opt_newpin; while (pin == NULL) { u8 *pin2; pin = get_pin("Enter new PIN", pin_obj); if (pinpad_present && pin == NULL) { if (verbose) printf("New PIN value will be prompted with pinpad.\n"); break; } if (pin == NULL || strlen((char *) pin) == 0) { free(pin); return 2; } pin2 = get_pin("Enter new PIN again", pin_obj); if (pin2 == NULL || strlen((char *) pin2) == 0) { free(pin); free(pin2); return 2; } if (strcmp((char *) pin, (char *) pin2) != 0) { printf("PIN codes do not match, try again.\n"); free(pin); pin = NULL; } free(pin2); } r = sc_pkcs15_unblock_pin(p15card, pin_obj, puk, puk ? strlen((char *) puk) : 0, pin, pin ? strlen((char *) pin) : 0); if (NULL == opt_puk) free(puk); if (NULL == opt_pin && NULL == opt_newpin) free(pin); if (r == SC_ERROR_PIN_CODE_INCORRECT) { fprintf(stderr, "PUK code incorrect; tries left: %d\n", pinfo->tries_left); return 3; } else if (r) { fprintf(stderr, "PIN unblocking failed: %s\n", sc_strerror(r)); return 2; } if (verbose) printf("PIN successfully unblocked.\n"); return 0; } static int change_pin(void) { sc_pkcs15_object_t *pin_obj; sc_pkcs15_auth_info_t *pinfo = NULL; u8 *pincode, *newpin; int r, pinpad_present = 0; pinpad_present = p15card->card->reader->capabilities & SC_READER_CAP_PIN_PAD || p15card->card->caps & SC_CARD_CAP_PROTECTED_AUTHENTICATION_PATH; if (!(pin_obj = get_pin_info())) return 2; pinfo = (sc_pkcs15_auth_info_t *) pin_obj->data; if (pinfo->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return 1; if (pinfo->tries_left != -1) { if (pinfo->tries_left != pinfo->max_tries) { if (pinfo->tries_left == 0) { fprintf(stderr, "PIN code blocked!\n"); return 2; } else { fprintf(stderr, "%d PIN tries left.\n", pinfo->tries_left); } } } pincode = (u8 *) opt_pin; if (pincode == NULL) { pincode = get_pin("Enter old PIN", pin_obj); if (!pinpad_present && pincode == NULL) return 2; } if (pincode && strlen((char *) pincode) == 0) { fprintf(stderr, "No PIN code supplied.\n"); if (opt_pin == NULL) free(pincode); return 2; } if (pincode == NULL && verbose) printf("Old PIN value will be prompted with pinpad.\n"); newpin = (u8 *) opt_newpin; while (newpin == NULL) { u8 *newpin2; newpin = get_pin("Enter new PIN", pin_obj); if (pinpad_present && newpin == NULL) { if (verbose) printf("New PIN value will be prompted with pinpad.\n"); break; } if (newpin == NULL || strlen((char *) newpin) == 0) { fprintf(stderr, "No new PIN value supplied.\n"); free(newpin); if (opt_pin == NULL) free(pincode); return 2; } newpin2 = get_pin("Enter new PIN again", pin_obj); if (newpin2 && strlen((char *) newpin2) && strcmp((char *) newpin, (char *) newpin2) == 0) { free(newpin2); break; } printf("PIN codes do not match, try again.\n"); free(newpin); free(newpin2); newpin=NULL; } r = sc_pkcs15_change_pin(p15card, pin_obj, pincode, pincode ? strlen((char *) pincode) : 0, newpin, newpin ? strlen((char *) newpin) : 0); if (r == SC_ERROR_PIN_CODE_INCORRECT) { fprintf(stderr, "PIN code incorrect; tries left: %d\n", pinfo->tries_left); return 3; } else if (r) { fprintf(stderr, "PIN code change failed: %s\n", sc_strerror(r)); return 2; } if (verbose) printf("PIN code changed successfully.\n"); if (opt_pin == NULL) free(pincode); if (opt_newpin == NULL) free(newpin); return 0; } static int test_update(sc_card_t *in_card) { sc_apdu_t apdu; static u8 cmd1[2] = { 0x50, 0x15}; u8 rbuf[258]; int rc; int r; static u8 fci_bad[] = { 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static u8 fci_good[] = { 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00 }; r = sc_lock(card); if (r < 0) return r; if (strcmp("cardos",in_card->driver->short_name) != 0) { printf("not using the cardos driver, card is fine.\n"); rc = 0; goto end; } /* first select file on 5015 and get fci */ sc_format_apdu(in_card, &apdu, SC_APDU_CASE_4_SHORT, 0xa4, 0x08, 0x00); apdu.lc = sizeof(cmd1); apdu.datalen = sizeof(cmd1); apdu.data = cmd1; apdu.le = 256; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); r = sc_transmit_apdu(card, &apdu); if (r < 0) { printf("selecting folder failed: %s\n", sc_strerror(r)); rc = 2; goto end; } if (apdu.sw1 != 0x90) { printf("apdu command select file failed: card returned %02X %02X\n", apdu.sw1, apdu.sw2); rc = 2; goto end; } if (apdu.resplen < 6) { printf("select file did not return enough data (length %d)\n", (int) apdu.resplen); goto bad_fci; } if (rbuf[0] != 0x6f) { printf("select file did not return the information we need\n"); goto bad_fci; } if (rbuf[1] != apdu.resplen -2) { printf("select file returned inconsistent information\n"); goto bad_fci; } { size_t i=0; while(i < rbuf[1]) { if (rbuf[2+i] == 0x86) { /* found our buffer */ break; } /* other tag */ i += 2 + rbuf[2+i+1]; /* length of this tag*/ if (2+i+1 >= apdu.resplen) { break; } } 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; } if (memcmp(&rbuf[2+i+2],fci_good,sizeof(fci_good)) == 0) { printf("fci is up-to-date, card is fine\n"); rc = 0; goto end; } if (memcmp(&rbuf[2+i+2],fci_bad,sizeof(fci_bad)) == 0) { printf("fci is out-of-date, card is vulnerable\n"); rc = 1; goto end; } printf("select file returned fci with unknown data\n"); goto bad_fci; } end: sc_unlock(card); /* 0 = card ok, 1 = card vulnerable, 2 = problem! */ return rc; bad_fci: sc_unlock(card); util_hex_dump(stdout,rbuf,apdu.resplen," "); printf("\n"); return 2; } static int update(sc_card_t *in_card) { sc_apdu_t apdu; u8 rbuf[258]; static u8 cmd1[2] = { 0x50, 0x15}; static u8 cmd3[11] = { 0x86, 0x09, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00}; int r; /* first select file on 5015 */ sc_format_apdu(in_card, &apdu, SC_APDU_CASE_3_SHORT, 0xa4, 0x08, 0x00); apdu.lc = sizeof(cmd1); apdu.datalen = sizeof(cmd1); apdu.data = cmd1; r = sc_lock(card); if (r < 0) return r; r = sc_transmit_apdu(card, &apdu); if (r < 0) { printf("selecting folder failed: %s\n", sc_strerror(r)); goto end; } if (apdu.sw1 != 0x90) { printf("apdu command select file: card returned %02X %02X\n", apdu.sw1, apdu.sw2); goto end; } /* next get lifecycle */ memset(&apdu, 0, sizeof(apdu)); sc_format_apdu(in_card, &apdu, SC_APDU_CASE_2, 0xca, 0x01, 0x83); apdu.cla = 0x00; apdu.le = 256; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); r = sc_transmit_apdu(card, &apdu); if (r < 0) { printf("get lifecycle failed: %s\n", sc_strerror(r)); goto end; } if (apdu.sw1 != 0x90) { printf("get lifecycle failed: card returned %02X %02X\n", apdu.sw1, apdu.sw2); goto end; } if (apdu.resplen < 1) { printf("get lifecycle failed: lifecycle byte not in response\n"); goto end; } if (rbuf[0] != 0x10 && rbuf[0] != 0x20) { printf("lifecycle neither user nor admin, can't proceed\n"); goto end; } if (rbuf[0] == 0x20) goto skip_change_lifecycle; /* next phase control / change lifecycle to operational */ memset(&apdu, 0, sizeof(apdu)); sc_format_apdu(in_card, &apdu, SC_APDU_CASE_1, 0x10, 0x00, 0x00); apdu.cla = 0x80; r = sc_transmit_apdu(card, &apdu); if (r < 0) { printf("change lifecycle failed: %s\n", sc_strerror(r)); goto end; } if (apdu.sw1 != 0x90) { printf("apdu command change lifecycle failed: card returned %02X %02X\n", apdu.sw1, apdu.sw2); goto end; } skip_change_lifecycle: /* last update AC */ memset(&apdu, 0, sizeof(apdu)); sc_format_apdu(in_card, &apdu, SC_APDU_CASE_3_SHORT, 0xda, 0x01, 0x6f); apdu.lc = sizeof(cmd3); apdu.datalen = sizeof(cmd3); apdu.data = cmd3; apdu.le = 0; apdu.resplen = 0; apdu.resp = NULL; r = sc_transmit_apdu(card, &apdu); if (r < 0) { printf("update fci failed: %s\n", sc_strerror(r)); goto end; } if (apdu.sw1 != 0x90) { printf("apdu command update fci failed: card returned %02X %02X\n", apdu.sw1, apdu.sw2); goto end; } printf("security update applied successfully.\n"); end: sc_unlock(card); return 0; } int main(int argc, char *argv[]) { int err = 0, r, c, long_optind = 0; int do_read_cert = 0; int do_list_certs = 0; int do_read_data_object = 0; int do_list_data_objects = 0; int do_list_pins = 0; int do_list_skeys = 0; int do_list_apps = 0; int do_dump = 0; int do_list_prkeys = 0; int do_list_pubkeys = 0; int do_read_pubkey = 0; #if defined(ENABLE_OPENSSL) && (defined(_WIN32) || defined(HAVE_INTTYPES_H)) int do_read_sshkey = 0; #endif int do_verify_pin = 0; int do_change_pin = 0; int do_unblock_pin = 0; int do_test_update = 0; int do_test_session_pin = 0; int do_update = 0; int do_print_version = 0; int do_list_info = 0; int action_count = 0; sc_context_param_t ctx_param; assert(sizeof(option_help)/sizeof(char *)==sizeof(options)/sizeof(struct option)); while (1) { c = getopt_long(argc, argv, "r:cuko:sva:LR:CwDTU", options, &long_optind); if (c == -1) break; 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++; break; case OPT_LIST_INFO: do_list_info = 1; action_count++; break; case OPT_READ_CERT: opt_cert = optarg; do_read_cert = 1; action_count++; break; case 'c': do_list_certs = 1; action_count++; break; case 'R': opt_data = optarg; do_read_data_object = 1; action_count++; break; case OPT_RAW: opt_raw = 1; break; case 'C': do_list_data_objects = 1; action_count++; break; case OPT_VERIFY_PIN: do_verify_pin = 1; action_count++; break; case OPT_CHANGE_PIN: do_change_pin = 1; action_count++; break; case 'u': do_unblock_pin = 1; action_count++; break; case OPT_LIST_PINS: do_list_pins = 1; action_count++; break; case OPT_LIST_SKEYS: do_list_skeys = 1; action_count++; break; case 'D': do_dump = 1; action_count++; break; case 'k': do_list_prkeys = 1; action_count++; break; case OPT_LIST_PUB: do_list_pubkeys = 1; action_count++; break; case OPT_READ_PUB: opt_pubkey = optarg; do_read_pubkey = 1; action_count++; break; #if defined(ENABLE_OPENSSL) && (defined(_WIN32) || defined(HAVE_INTTYPES_H)) case OPT_READ_SSH: opt_pubkey = optarg; do_read_sshkey = 1; action_count++; break; case OPT_RFC4716: opt_rfc4716 = 1; break; #endif case 'T': do_test_update = 1; action_count++; break; case OPT_TEST_SESSION_PIN: do_test_session_pin = 1; action_count++; break; case 'U': do_update = 1; action_count++; break; case OPT_READER: opt_reader = optarg; break; case OPT_PIN: util_get_pin(optarg, &opt_pin); break; case OPT_NEWPIN: util_get_pin(optarg, &opt_newpin); break; case OPT_PUK: util_get_pin(optarg, &opt_puk); break; case 'o': opt_outfile = optarg; break; case 's': compact++; break; case 'v': verbose++; break; case 'a': opt_auth_id = optarg; break; case OPT_BIND_TO_AID: opt_bind_to_aid = optarg; break; case OPT_LIST_APPLICATIONS: do_list_apps = 1; action_count++; break; case OPT_NO_CACHE: opt_no_cache++; break; case OPT_CLEAR_CACHE: opt_clear_cache = 1; action_count++; break; case 'w': opt_wait = 1; break; case OPT_USE_PINPAD_DEPRECATED: fprintf(stderr, "'--no-prompt' is deprecated , use '--use-pinpad' instead.\n"); /* fallthrough */ case OPT_USE_PINPAD: opt_use_pinpad = 1; break; } } 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); action_count--; } 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", sc_strerror(r)); return 1; } if (opt_clear_cache) { if ((err = clear_cache())) goto end; action_count--; } err = util_connect_card_ex(ctx, &card, opt_reader, opt_wait, 0); if (err) goto end; if (verbose) fprintf(stderr, "Trying to find a PKCS#15 compatible card...\n"); if (opt_bind_to_aid) { struct sc_aid aid; 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); err = 1; goto end; } r = sc_pkcs15_bind(card, &aid, &p15card); } else { r = sc_pkcs15_bind(card, NULL, &p15card); } if (r) { fprintf(stderr, "PKCS#15 binding failed: %s\n", sc_strerror(r)); err = 1; goto end; } if (opt_no_cache) p15card->opts.use_file_cache = 0; if (verbose) fprintf(stderr, "Found %s!\n", p15card->tokeninfo->label); if (do_list_info) { if (!do_dump) list_info(); action_count--; } if (do_verify_pin) if ((err = verify_pin())) goto end; if (do_list_certs) { if ((err = list_certificates())) goto end; action_count--; } if (do_read_cert) { if ((err = read_certificate())) goto end; action_count--; } if (do_list_data_objects) { if ((err = list_data_objects())) goto end; action_count--; } if (do_read_data_object) { if ((err = read_data_object())) goto end; action_count--; } if (do_list_prkeys) { if ((err = list_private_keys())) goto end; action_count--; } if (do_list_pubkeys) { if ((err = list_public_keys())) goto end; action_count--; } if (do_read_pubkey) { if ((err = read_public_key())) goto end; action_count--; } #if defined(ENABLE_OPENSSL) && (defined(_WIN32) || defined(HAVE_INTTYPES_H)) if (do_read_sshkey) { if ((err = read_ssh_key())) goto end; action_count--; } #endif if (do_list_pins) { if ((err = list_pins())) goto end; action_count--; } if (do_list_skeys) { if ((err = list_skeys())) goto end; action_count--; } if (do_list_apps) { if ((err = list_apps(stdout))) goto end; action_count--; } if (do_dump) { if ((err = dump())) goto end; action_count--; } if (do_change_pin) { if ((err = change_pin())) goto end; action_count--; } if (do_unblock_pin) { if ((err = unblock_pin())) goto end; action_count--; } if (do_test_update || do_update) { err = test_update(card); action_count--; if (err == 2) { /* problem */ err = 1; goto end; } if (do_update && err == 1) { /* card vulnerable */ if ((err = update(card))) goto end; } } if (do_test_session_pin) { if ((err = test_session_pin())) goto end; action_count--; } end: sc_pkcs15_unbind(p15card); sc_disconnect_card(card); sc_release_context(ctx); return err; } OpenSC-0.26.1/src/tools/sc-hsm-tool.c000066400000000000000000001541251474147347300172150ustar00rootroot00000000000000/* * sc-hsm-tool.c: SmartCard-HSM Management Tool * * 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 * 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 #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include /* Requires openssl for dkek import */ #include #include #include #include #include #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" static const char *app_name = "sc-hsm-tool"; static const char magic[] = "Salted__"; static struct sc_aid sc_hsm_aid = { { 0xE8,0x2B,0x06,0x01,0x04,0x01,0x81,0xC3,0x1F,0x02,0x01 }, 11 }; static int opt_wait = 0; static char *opt_reader = NULL; static char *opt_label = NULL; static int verbose = 0; // Some reasonable maximums #define MAX_CERT 4096 #define MAX_PRKD 256 #define MAX_KEY 1500 #define MAX_WRAPPED_KEY (MAX_CERT + MAX_PRKD + MAX_KEY) #define SEED_LENGTH 16 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' }, { "import-dkek-share", 1, NULL, 'I' }, #ifdef PRINT_DKEK_SHARE { "print-dkek-share", 1, NULL, 'P' }, #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 }, { "key-reference", 1, NULL, 'i' }, { "label", 1, NULL, 'l' }, { "force", 0, NULL, 'f' }, { "reader", 1, NULL, 'r' }, { "wait", 0, NULL, 'w' }, { "verbose", 0, NULL, 'v' }, { NULL, 0, NULL, 0 } }; // clang-format on static const char *option_help[] = { "Initialize token", "Create DKEK key share and save to ", "Import DKEK key share ", #ifdef PRINT_DKEK_SHARE "Print HEX of DKEK key share ", #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/export", "Token label for --initialize", "Force replacement of key and certificate", "Uses reader number [0]", "Wait for a card to be inserted", "Verbose operation, may be used several times", }; typedef struct { BIGNUM * x; BIGNUM * y; } secret_share_t; /** * Generate a prime number * * The internal CPRNG is seeded using the provided seed value. * * @param prime Pointer for storage of prime number * @param s Secret to share * @param bits Bit size of prime * @param rngSeed Seed value for CPRNG * @param rngSeedLength Length of Seed value for CPRNG * */ static int generatePrime(BIGNUM *prime, const BIGNUM *s, const int bits, unsigned char *rngSeed, const unsigned int rngSeedLength) { int max_rounds = 1000; // Seed the RNG RAND_seed(rngSeed, rngSeedLength); // Clear the prime value BN_clear(prime); do { // Generate random prime BN_generate_prime_ex(prime, bits, 1, NULL, NULL, NULL); } while ((BN_ucmp(prime, s) == -1) && (max_rounds-- > 0)); // If prime < s or not reached 1000 tries if (max_rounds > 0) return 0; else return -1; // We could not find a prime number } /** * Helper method to calculate the y-value * for a given x-value and a polynomial * * @param x X-value * @param polynomial The underlying polynomial * @param t Threshold (determines the degree of the polynomial) * @param prime Prime for finite field arithmetic * @param y Pointer for storage of calculated y-value */ static void calculatePolynomialValue(const BIGNUM *x, BIGNUM **polynomial, const unsigned char t, const BIGNUM *prime, BIGNUM *y) { BIGNUM **pp; BIGNUM *temp; BIGNUM *exponent; unsigned long exp; BN_CTX *bn_ctx; // Create context for temporary variables of OpenSSL engine bn_ctx = BN_CTX_new(); temp = BN_new(); exponent = BN_new(); // Set y to ZERO BN_zero(y); /* Initialize the result using the secret value at position 0 of the polynomial */ pp = polynomial; BN_copy(y, *pp); pp++; for (exp = 1; exp < t; exp++) { BN_copy(temp, x); BN_set_word(exponent, exp); // temp = x^exponent mod prime BN_mod_exp(temp, x, exponent, prime, bn_ctx); // exponent = temp * a = a * x^exponent mod prime 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, bn_ctx); pp++; } BN_clear_free(temp); BN_clear_free(exponent); BN_CTX_free(bn_ctx); } /** * Create shares depending on the provided parameters * * @param s Secret value to share * @param t Threshold needed to reconstruct the secret * @param n Total number of shares * @param prime Prime for finite field arithmetic * @param shares Pointer for storage of calculated shares (must be big enough to hold n shares) */ static int createShares(const BIGNUM *s, const unsigned char t, const unsigned char n, const BIGNUM *prime, secret_share_t *shares) { // Array representing the polynomial a(x) = s + a_1 * x + ... + a_n-1 * x^n-1 mod p BIGNUM **polynomial = malloc(n * sizeof(BIGNUM *)); BIGNUM **pp; unsigned long i; secret_share_t *sp; if (!polynomial) return -1; // Set the secret value as the constant part of the polynomial pp = polynomial; *pp = BN_new(); BN_copy(*pp, s); pp++; // Initialize and generate some random values for coefficients a_x in the remaining polynomial for (i = 1; i < t; i++) { *pp = BN_new(); BN_rand_range(*pp, prime); pp++; } sp = shares; // Now calculate n secret shares for (i = 1; i <= n; i++) { sp->x = BN_new(); sp->y = BN_new(); BN_set_word((sp->x), i); calculatePolynomialValue(sp->x, polynomial, t, prime, (sp->y)); sp++; } // Deallocate the resource of the polynomial pp = polynomial; for (i = 0; i < t; i++) { BN_clear_free(*pp); pp++; } free(polynomial); return 0; } /** * Reconstruct secret using the provided shares * * @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 secret */ static int reconstructSecret(secret_share_t *shares, unsigned char t, const BIGNUM *prime, BIGNUM *s) { unsigned char i; unsigned char j; // Array representing the polynomial a(x) = s + a_1 * x + ... + a_n-1 * x^n-1 mod p BIGNUM **bValue = malloc(t * sizeof(BIGNUM *)); BIGNUM **pbValue; BIGNUM * numerator; BIGNUM * denominator; BIGNUM * temp; secret_share_t *sp_i; secret_share_t *sp_j; BN_CTX *ctx; if (!bValue) return -1; // Initialize pbValue = bValue; for (i = 0; i < t; i++) { *pbValue = BN_new(); pbValue++; } numerator = BN_new(); denominator = BN_new(); temp = BN_new(); // Create context for temporary variables of engine ctx = BN_CTX_new(); pbValue = bValue; sp_i = shares; for (i = 0; i < t; i++) { BN_one(numerator); BN_one(denominator); sp_j = shares; for (j = 0; j < t; j++) { if (i == j) { sp_j++; continue; } BN_mul(numerator, numerator, (sp_j->x), ctx); BN_sub(temp, (sp_j->x), (sp_i->x)); BN_mul(denominator, denominator, temp, ctx); sp_j++; } /* * Use the modular inverse value of the denominator for the * multiplication */ if (BN_mod_inverse(denominator, denominator, prime, ctx) == NULL ) { free(bValue); return -1; } BN_mod_mul(*pbValue, numerator, denominator, prime, ctx); pbValue++; sp_i++; } /* * Calculate the secret by multiplying all y-values with their * corresponding intermediate values */ pbValue = bValue; sp_i = shares; BN_zero(s); for (i = 0; i < t; i++) { BN_mul(temp, (sp_i->y), *pbValue, ctx); BN_add(s, s, temp); pbValue++; sp_i++; } // Perform modulo operation and copy result BN_nnmod(temp, s, prime, ctx); BN_copy(s, temp); BN_clear_free(numerator); BN_clear_free(denominator); BN_clear_free(temp); BN_CTX_free(ctx); // Deallocate the resource of the polynomial pbValue = bValue; for (i = 0; i < t; i++) { BN_clear_free(*pbValue); pbValue++; } free(bValue); return 0; } /** * Helper method to free allocated resources * * @param shares Shares to be freed * @param n Total number of shares to freed */ static int cleanUpShares(secret_share_t *shares, unsigned char n) { int i; secret_share_t *sp; sp = shares; for (i = 0; i < n; i++) { BN_clear_free((sp->x)); BN_clear_free((sp->y)); sp++; } free(shares); return 0; } void clearScreen() { if (system( "clear" )) { if (system( "cls" )) { fprintf(stderr, "Clearing the screen failed\n"); } } } void waitForEnterKeyPressed() { int c; fflush(stdout); while ((c = getchar()) != '\n' && c != EOF) { } } static void print_dkek_info(sc_cardctl_sc_hsm_dkek_t *dkekinfo) { printf("DKEK shares : %d\n", dkekinfo->dkek_shares); if (dkekinfo->outstanding_shares > 0) { printf("DKEK import pending, %d share(s) still missing\n",dkekinfo->outstanding_shares); } else { printf("DKEK key check value : "); util_hex_dump(stdout, dkekinfo->key_check_value, 8, NULL); printf("\n"); } } static void print_info(sc_card_t *card, sc_file_t *file) { int r, tries_left; struct sc_pin_cmd_data data; sc_cardctl_sc_hsm_dkek_t dkekinfo; u8 major, minor, opt; major = file->prop_attr[file->prop_attr_len - 2]; minor = file->prop_attr[file->prop_attr_len - 1]; printf("Version : %d.%d\n", (int)major, (int)minor); if (file->prop_attr_len > 2) { /* Version >= 2.0 */ opt = file->prop_attr[file->prop_attr_len - 4]; if (opt != 0) { printf("Config options :\n"); if (opt & INIT_RRC_ENABLED) { printf(" User PIN reset with SO-PIN enabled\n"); } if (opt & INIT_TRANSPORT_PIN) { printf(" Transport-PIN mode enabled\n"); } } /* Try to update SO-PIN info from card */ memset(&data, 0, sizeof(data)); data.cmd = SC_PIN_CMD_GET_INFO; data.pin_type = SC_AC_CHV; data.pin_reference = ID_SO_PIN; r = sc_pin_cmd(card, &data, &tries_left); 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) { printf("SO-PIN locked\n"); } else { printf("SO-PIN tries left : %d\n", tries_left); } /* 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_reference = ID_USER_PIN; r = sc_pin_cmd(card, &data, &tries_left); 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) { printf("User PIN locked\n"); } else { printf("User PIN tries left : %d\n", tries_left); } } } } else { /* Version < 2.0 */ /* 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_reference = ID_USER_PIN; r = sc_pin_cmd(card, &data, &tries_left); 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) { printf("User PIN locked\n"); } else { printf("User PIN tries left : %d\n", tries_left); } } } memset(&dkekinfo, 0, sizeof(dkekinfo)); r = sc_card_ctl(card, SC_CARDCTL_SC_HSM_IMPORT_DKEK_SHARE, (void *)&dkekinfo); if (r == SC_ERROR_INS_NOT_SUPPORTED) { // Not supported or not initialized for key shares return; } if (r < 0) { fprintf(stderr, "sc_card_ctl(*, SC_CARDCTL_SC_HSM_IMPORT_DKEK_SHARE, *) failed with %s\n", sc_strerror(r)); } print_dkek_info(&dkekinfo); } 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); printf("\n"); } else { _so_pin = (char *)so_pin; } len = sizeof(param.init_code); r = sc_hex_to_bin(_so_pin, param.init_code, &len); if (r < 0) { fprintf(stderr, "Error decoding initialization code (%s)\n", sc_strerror(r)); return -1; } if (len != 8) { fprintf(stderr, "SO-PIN must be a hexadecimal string of 16 characters\n"); return -1; } if (user_pin == NULL) { printf("Enter initial User-PIN (6 - 16 characters) : "); util_getpass(&_user_pin, NULL, stdin); printf("\n"); } else { _user_pin = (char *)user_pin; } param.user_pin_len = strlen(_user_pin); if (param.user_pin_len < 6) { fprintf(stderr, "PIN must be at least 6 characters long\n"); return -1; } if (param.user_pin_len > 16) { fprintf(stderr, "PIN must not be longer than 16 characters\n"); return -1; } if ((param.user_pin_len == 6) && (retry_counter > 3)) { fprintf(stderr, "Retry counter must not exceed 3 for a 6 digit PIN. Use a longer PIN for a higher retry counter.\n"); return -1; } if ((param.user_pin_len == 7) && (retry_counter > 5)) { fprintf(stderr, "Retry counter must not exceed 5 for a 7 digit PIN. Use a longer PIN for a higher retry counter.\n"); return -1; } if (retry_counter > 10) { fprintf(stderr, "Retry counter must not exceed 10\n"); return -1; } param.user_pin = (u8 *)_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; /* 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); if (r < 0) { fprintf(stderr, "sc_card_ctl(*, SC_CARDCTL_SC_HSM_INITIALIZE, *) failed with %s\n", sc_strerror(r)); } return 0; } static int recreate_password_from_shares(char **pwd, int *pwdlen, int num_of_password_shares) { int r, i; BIGNUM *prime; BIGNUM *secret; BIGNUM *p; char inbuf[64]; unsigned char bin[64]; size_t binlen = 0; unsigned char *ip; secret_share_t *shares = NULL; secret_share_t *sp; if (num_of_password_shares < 2) { fprintf(stderr, "--pwd-shares-total must 2 or larger\n"); return -1; } // Allocate data buffer for the shares shares = malloc(num_of_password_shares * sizeof(secret_share_t)); if (!shares) return -1; /* * Initialize prime and secret */ prime = BN_new(); secret = BN_new(); printf("\nDeciphering the DKEK for import into the SmartCard-HSM requires %i key custodians", num_of_password_shares); printf("\nto present their share. Only the first key custodian needs to enter the public prime."); printf("\nPlease remember to present the share id as well as the share value."); printf("\n\nPlease enter prime: "); memset(inbuf, 0, sizeof(inbuf)); if (fgets(inbuf, sizeof(inbuf), stdin) == NULL) { fprintf(stderr, "Input aborted\n"); free(shares); return -1; } binlen = 64; sc_hex_to_bin(inbuf, bin, &binlen); BN_bin2bn(bin, (int)binlen, prime); sp = shares; for (i = 0; i < num_of_password_shares; i++) { clearScreen(); printf("Press to enter share %i of %i\n\n", i + 1, num_of_password_shares); waitForEnterKeyPressed(); clearScreen(); sp->x = BN_new(); sp->y = BN_new(); printf("Share %i of %i\n\n", i + 1, num_of_password_shares); printf("Please enter share ID: "); memset(inbuf, 0, sizeof(inbuf)); if (fgets(inbuf, sizeof(inbuf), stdin) == NULL) { fprintf(stderr, "Input aborted\n"); free(shares); return -1; } p = (sp->x); BN_hex2bn(&p, inbuf); printf("Please enter share value: "); memset(inbuf, 0, sizeof(inbuf)); if (fgets(inbuf, sizeof(inbuf), stdin) == NULL) { fprintf(stderr, "Input aborted\n"); free(shares); return -1; } binlen = 64; sc_hex_to_bin(inbuf, bin, &binlen); BN_bin2bn(bin, (int)binlen, (sp->y)); sp++; } 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); return r; } /* * Encode the secret value */ ip = (unsigned char *) inbuf; *pwdlen = BN_bn2bin(secret, ip); *pwd = calloc(1, *pwdlen); if (*pwd) { memcpy(*pwd, ip, *pwdlen); } cleanUpShares(shares, num_of_password_shares); BN_clear_free(prime); BN_clear_free(secret); return *pwd ? 0 : -1; } 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 *bn_ctx = NULL; FILE *in = NULL; u8 filebuff[64],key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH],outbuff[64]; char *pwd = NULL; int r, outlen, pwdlen; if (inf == NULL) { fprintf(stderr, "No file name specified for DKEK share\n"); return -1; } in = fopen(inf, "rb"); if (in == NULL) { perror(inf); return -1; } if (fread(filebuff, 1, sizeof(filebuff), in) != sizeof(filebuff)) { perror(inf); fclose(in); return -1; } fclose(in); if (memcmp(filebuff, magic, sizeof(magic) - 1)) { fprintf(stderr, "File %s is not a DKEK share\n", inf); return -1; } if (password == NULL) { if (num_of_password_shares == -1) { printf("Enter password to decrypt DKEK share : "); util_getpass(&pwd, NULL, stdin); 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; } } } else { pwd = (char *) password; pwdlen = (int)strlen(password); } printf("Deciphering DKEK share, please wait...\n"); EVP_BytesToKey(EVP_aes_256_cbc(), EVP_md5(), filebuff + 8, (u8 *)pwd, pwdlen, iter, key, iv); OPENSSL_cleanse(pwd, strlen(pwd)); if (password == NULL) { free(pwd); } 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)); dkekinfo.importShare = 1; OPENSSL_cleanse(outbuff, sizeof(outbuff)); r = sc_card_ctl(card, SC_CARDCTL_SC_HSM_IMPORT_DKEK_SHARE, (void *)&dkekinfo); OPENSSL_cleanse(&dkekinfo.dkek_share, sizeof(dkekinfo.dkek_share)); 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"); return -1; } if (r < 0) { fprintf(stderr, "sc_card_ctl(*, SC_CARDCTL_SC_HSM_IMPORT_DKEK_SHARE, *) failed with %s\n", sc_strerror(r)); return -1; } printf("DKEK share imported\n"); print_dkek_info(&dkekinfo); return 0; } 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 // decrypt_keyblob.js file sc_cardctl_sc_hsm_dkek_t dkekinfo; 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; int r, outlen, pwdlen; u8 i; if (inf == NULL) { fprintf(stderr, "No file name specified for DKEK share\n"); return -1; } in = fopen(inf, "rb"); if (in == NULL) { perror(inf); return -1; } if (fread(filebuff, 1, sizeof(filebuff), in) != sizeof(filebuff)) { perror(inf); fclose(in); return -1; } fclose(in); if (memcmp(filebuff, magic, sizeof(magic) - 1)) { fprintf(stderr, "File %s is not a DKEK share\n", inf); return -1; } if (password == NULL) { if (num_of_password_shares == -1) { printf("Enter password to decrypt DKEK share : "); util_getpass(&pwd, NULL, stdin); pwdlen = (int)strlen(pwd); printf("\n"); } else { r = recreate_password_from_shares(&pwd, &pwdlen, num_of_password_shares); if (r < 0) { return -1; } } } else { pwd = (char *) password; pwdlen = (int)strlen(password); } printf("Deciphering DKEK share, please wait...\n"); EVP_BytesToKey(EVP_aes_256_cbc(), EVP_md5(), filebuff + 8, (u8 *)pwd, pwdlen, iter, key, iv); OPENSSL_cleanse(pwd, strlen(pwd)); if (password == NULL) { free(pwd); } 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)); dkekinfo.importShare = 1; OPENSSL_cleanse(outbuff, sizeof(outbuff)); printf("DKEK Share HEX: \n\n"); for (i = 0; i < sizeof(dkekinfo.dkek_share); i++) { printf("%02X", dkekinfo.dkek_share[i]); } printf("\n\n"); OPENSSL_cleanse(&dkekinfo.dkek_share, sizeof(dkekinfo.dkek_share)); 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"); return -1; } if (r < 0) { fprintf(stderr, "sc_card_ctl(*, SC_CARDCTL_SC_HSM_IMPORT_DKEK_SHARE, *) failed with %s\n", sc_strerror(r)); return -1; } //printf("DKEK share imported\n"); //print_dkek_info(&dkekinfo); return 0; } static void ask_for_password(char **pwd, int *pwdlen) { char *refpwd = NULL; printf( "\nThe DKEK share will be enciphered using a key derived from a user supplied password.\n"); printf( "The security of the DKEK share relies on a well chosen and sufficiently long password.\n"); printf( "The recommended length is more than 10 characters, which are mixed letters, numbers and\n"); printf("symbols.\n\n"); printf( "Please keep the generated DKEK share file in a safe location. We also recommend to keep a\n"); printf( "paper printout, in case the electronic version becomes unavailable. A printable version\n"); printf( "of the file can be generated using \"openssl base64 -in \".\n"); while (1) { printf("Enter password to encrypt DKEK share : "); util_getpass(pwd, NULL, stdin); printf("\n"); if (strlen(*pwd) < 6) { printf("Password way to short. Please retry.\n"); continue; } printf("Please retype password to confirm : "); util_getpass(&refpwd, NULL, stdin); printf("\n"); if (strcmp(*pwd, refpwd)) { printf("Passwords do not match. Please retry.\n"); continue; } *pwdlen = (int)strlen(*pwd); break; } OPENSSL_cleanse(refpwd, strlen(refpwd)); free(refpwd); } static int generate_pwd_shares(sc_card_t *card, char **pwd, int *pwdlen, int password_shares_threshold, int password_shares_total) { int r, i; BIGNUM *prime; BIGNUM *secret; unsigned char buf[64]; char hex[64]; int l; secret_share_t *shares = NULL; secret_share_t *sp; u8 rngseed[16]; if ((password_shares_threshold == -1) || (password_shares_total == -1)) { fprintf(stderr, "Must specify both, --pwd-shares-total and --pwd-shares-threshold\n"); return -1; } if (password_shares_total < 3) { fprintf(stderr, "--pwd-shares-total must be 3 or larger\n"); return -1; } if (password_shares_threshold < 2) { fprintf(stderr, "--pwd-shares-threshold must 2 or larger\n"); return -1; } if (password_shares_threshold > password_shares_total) { fprintf(stderr, "--pwd-shares-threshold must be smaller or equal to --pwd-shares-total\n"); return -1; } printf( "\nThe DKEK will be enciphered using a randomly generated 64 bit password.\n"); printf( "This password is split using a (%i-of-%i) threshold scheme.\n\n", password_shares_threshold, password_shares_total); printf( "Please keep the generated and encrypted DKEK file in a safe location. We also recommend \n"); printf( "to keep a paper printout, in case the electronic version becomes unavailable. A printable version\n"); printf( "of the file can be generated using \"openssl base64 -in \".\n"); printf("\n\nPress to continue"); waitForEnterKeyPressed(); *pwd = calloc(1, 8); *pwdlen = 8; r = sc_get_challenge(card, (unsigned char *)*pwd, 8); if (r < 0) { printf("Error generating random key failed with %s", sc_strerror(r)); OPENSSL_cleanse(*pwd, *pwdlen); free(*pwd); return r; } **pwd &= 0x7F; // Make sure the bit size of the secret is not bigger than 63 bits /* * Initialize prime and secret */ prime = BN_new(); secret = BN_new(); /* * Encode the secret value */ BN_bin2bn((unsigned char *)*pwd, *pwdlen, secret); /* * Generate seed and calculate a prime depending on the size of the secret */ 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; } 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); return r; } // Allocate data buffer for the generated shares shares = malloc(password_shares_total * sizeof(secret_share_t)); 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++) { clearScreen(); printf("Press to display key share %i of %i\n\n", i + 1, password_shares_total); waitForEnterKeyPressed(); clearScreen(); printf("Share %i of %i\n\n", i + 1, password_shares_total); l = BN_bn2bin(prime, buf); sc_bin_to_hex(buf, l, hex, 64, ':'); printf("\nPrime : %s\n", hex); printf("Share ID : %s\n", BN_bn2dec((sp->x))); l = BN_bn2bin((sp->y), buf); sc_bin_to_hex(buf, l, hex, 64, ':'); printf("Share value : %s\n", hex); printf("\n\nPlease note ALL values above and press when finished"); waitForEnterKeyPressed(); sp++; } clearScreen(); cleanUpShares(shares, password_shares_total); BN_clear_free(prime); BN_clear_free(secret); return 0; } 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 *c_ctx = NULL; FILE *out = NULL; u8 filebuff[64], key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH]; u8 dkek_share[32]; char *pwd = NULL; int r = 0, outlen, pwdlen = 0; if (outf == NULL) { fprintf(stderr, "No file name specified for DKEK share\n"); return -1; } if (password == NULL) { if ((password_shares_threshold == -1) && (password_shares_total == -1)) { ask_for_password(&pwd, &pwdlen); } else { // create password using threshold scheme r = generate_pwd_shares(card, &pwd, &pwdlen, password_shares_threshold, password_shares_total); } } else { pwd = (char *) password; pwdlen = (int)strlen(password); } if (r < 0) { fprintf(stderr, "Creating DKEK share failed\n"); return -1; } memcpy(filebuff, magic, sizeof(magic) - 1); r = sc_get_challenge(card, filebuff + 8, 8); if (r < 0) { fprintf(stderr, "Error generating random number failed with %s\n", sc_strerror(r)); return -1; } printf("Enciphering DKEK share, please wait...\n"); EVP_BytesToKey(EVP_aes_256_cbc(), EVP_md5(), filebuff + 8, (u8 *)pwd, pwdlen, iter, key, iv); if (password == NULL) { OPENSSL_cleanse(pwd, pwdlen); free(pwd); } r = sc_get_challenge(card, dkek_share, sizeof(dkek_share)); if (r < 0) { fprintf(stderr, "Error generating random number failed with %s\n", sc_strerror(r)); return -1; } 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"); if (out == NULL) { perror(outf); return -1; } if (fwrite(filebuff, 1, sizeof(filebuff), out) != sizeof(filebuff)) { perror(outf); fclose(out); return -1; } fclose(out); OPENSSL_cleanse(dkek_share, sizeof(dkek_share)); printf("DKEK share created and saved to %s\n", outf); return 0; } static size_t determineLength(const u8 *tlv, size_t buflen) { const u8 *ptr = tlv; unsigned int cla,tag; size_t len; if (sc_asn1_read_tag(&ptr, buflen, &cla, &tag, &len) != SC_SUCCESS || ptr == NULL) { return 0; } return len + (ptr - tlv); } /** * Encapsulate data object as TLV object * * @param tag the one byte tag * @param indata the value field * @param inlen the length of the value field * @param outdata pointer to the allocated memory buffer * @param outlen the size of the TLV object */ static int wrap_with_tag(u8 tag, u8 *indata, size_t inlen, u8 **outdata, size_t *outlen) { 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; u8 *ptr = calloc(r, sizeof *ptr); if (ptr == NULL) { return SC_ERROR_OUT_OF_MEMORY; } *outdata = ptr; *outlen = r; return sc_asn1_put_tag(tag, indata, inlen, *outdata, *outlen, NULL); } 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; FILE *out = NULL; u8 fid[2]; u8 ef_prkd[MAX_PRKD]; u8 ef_cert[MAX_CERT]; u8 wrapped_key_buff[MAX_KEY]; u8 keyblob[MAX_WRAPPED_KEY]; u8 *key; u8 *ptr; char *lpin = NULL; size_t key_len; int r, ef_prkd_len, ef_cert_len; if ((keyid < 1) || (keyid > 255)) { fprintf(stderr, "Invalid key reference (must be 0 < keyid <= 255)\n"); return -1; } if (outf == NULL) { fprintf(stderr, "No file name specified for wrapped key\n"); return -1; } if (pin == NULL) { printf("Enter User PIN : "); util_getpass(&lpin, NULL, stdin); printf("\n"); } else { lpin = (char *)pin; } memset(&data, 0, sizeof(data)); data.cmd = SC_PIN_CMD_VERIFY; data.pin_type = SC_AC_CHV; data.pin_reference = ID_USER_PIN; data.pin1.data = (unsigned char *)lpin; data.pin1.len = strlen(lpin); r = sc_pin_cmd(card, &data, NULL); if (r < 0) { fprintf(stderr, "PIN verification failed with %s\n", sc_strerror(r)); return -1; } if (pin == NULL) { free(lpin); } wrapped_key.key_id = keyid; wrapped_key.wrapped_key = wrapped_key_buff; wrapped_key.wrapped_key_length = sizeof(wrapped_key_buff); r = sc_card_ctl(card, SC_CARDCTL_SC_HSM_WRAP_KEY, (void *)&wrapped_key); 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; } if (r < 0) { fprintf(stderr, "sc_card_ctl(*, SC_CARDCTL_SC_HSM_WRAP_KEY, *) failed with %s\n", sc_strerror(r)); return -1; } fid[0] = PRKD_PREFIX; fid[1] = (unsigned char)keyid; ef_prkd_len = 0; /* 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); if (r == SC_SUCCESS) { ef_prkd_len = sc_read_binary(card, 0, ef_prkd, sizeof(ef_prkd), 0); 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 = (int)determineLength(ef_prkd, ef_prkd_len); } } 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, NULL); if (r == SC_SUCCESS) { ef_cert_len = sc_read_binary(card, 0, ef_cert, sizeof(ef_cert), 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); } } ptr = keyblob; // Encode key in octet string object key_len = 0; r = wrap_with_tag(0x04, wrapped_key.wrapped_key, wrapped_key.wrapped_key_length, &key, &key_len); LOG_TEST_RET(ctx, r, "Out of memory"); memcpy(ptr, key, key_len); ptr += key_len; free(key); key = NULL; key_len = 0; // Add private key description if (ef_prkd_len > 0) { memcpy(ptr, ef_prkd, ef_prkd_len); ptr += ef_prkd_len; } // Add certificate if (ef_cert_len > 0) { memcpy(ptr, ef_cert, ef_cert_len); ptr += ef_cert_len; } // Encode key, key description and certificate object in sequence r = wrap_with_tag(0x30, keyblob, ptr - keyblob, &key, &key_len); LOG_TEST_RET(ctx, r, "Out of memory"); out = fopen(outf, "wb"); if (out == NULL) { perror(outf); free(key); return -1; } if (fwrite(key, 1, key_len, out) != key_len) { perror(outf); free(key); fclose(out); return -1; } free(key); fclose(out); return 0; } static int update_ef(sc_card_t *card, u8 prefix, u8 id, int erase, const u8 *buf, size_t buflen) { sc_file_t *file = NULL; sc_path_t path; u8 fid[2]; int r; fid[0] = prefix; fid[1] = id; sc_path_set(&path, SC_PATH_TYPE_FILE_ID, fid, 2, 0, -1); r = sc_select_file(card, &path, NULL); if ((r == SC_SUCCESS) && erase) { sc_delete_file(card, &path); r = SC_ERROR_FILE_NOT_FOUND; } if (r == SC_ERROR_FILE_NOT_FOUND) { file = sc_file_new(); file->id = (path.value[0] << 8) | path.value[1]; file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = SC_FILE_EF_TRANSPARENT; file->size = (size_t) 0; file->status = SC_FILE_STATUS_ACTIVATED; r = sc_create_file(card, file); sc_file_free(file); if (r < 0) { return r; } } r = sc_update_binary(card, 0, buf, buflen, 0); return r; } static int unwrap_key(sc_card_t *card, int keyid, const char *inf, const char *pin, int force) { 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; sc_path_t path; u8 fid[2]; char *lpin = NULL; unsigned int cla, tag; int r; size_t keybloblen; size_t len, olen, prkd_len, cert_len; ssize_t sz; if ((keyid < 1) || (keyid > 255)) { fprintf(stderr, "Invalid key reference (must be 0 < keyid <= 255)\n"); return -1; } if (inf == NULL) { fprintf(stderr, "No file name specified for wrapped key\n"); return -1; } in = fopen(inf, "rb"); if (in == NULL) { perror(inf); return -1; } sz = fread(keyblob, 1, sizeof(keyblob), in); fclose(in); if (sz < 0) { perror(inf); return -1; } keybloblen = sz; 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) ){ fprintf(stderr, "Invalid wrapped key format (Outer sequence).\n"); return -1; } 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; } wrapped_key.wrapped_key = (u8 *)ptr; wrapped_key.wrapped_key_length = olen; ptr += olen; prkd = ptr; prkd_len = determineLength(ptr, keybloblen - (ptr - keyblob)); ptr += prkd_len; cert = ptr; cert_len = determineLength(ptr, keybloblen - (ptr - keyblob)); printf("Wrapped key contains:\n"); printf(" Key blob\n"); if (prkd_len > 0) { printf(" Private Key Description (PRKD)\n"); } if (cert_len > 0) { printf(" Certificate\n"); } if ((prkd_len > 0) && !force) { fid[0] = PRKD_PREFIX; fid[1] = (unsigned char)keyid; /* 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); 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; } } if ((cert_len > 0) && !force) { fid[0] = EE_CERTIFICATE_PREFIX; fid[1] = (unsigned char)keyid; /* Try to select a related EF containing the certificate */ sc_path_set(&path, SC_PATH_TYPE_FILE_ID, fid, sizeof(fid), 0, 0); r = sc_select_file(card, &path, NULL); 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; } } if (pin == NULL) { printf("Enter User PIN : "); util_getpass(&lpin, NULL, stdin); printf("\n"); } else { lpin = (char *)pin; } memset(&data, 0, sizeof(data)); data.cmd = SC_PIN_CMD_VERIFY; data.pin_type = SC_AC_CHV; data.pin_reference = ID_USER_PIN; data.pin1.data = (u8 *)lpin; data.pin1.len = strlen(lpin); r = sc_pin_cmd(card, &data, NULL); if (r < 0) { fprintf(stderr, "PIN verification failed with %s\n", sc_strerror(r)); return -1; } if (pin == NULL) { free(lpin); } if (force) { fid[0] = KEY_PREFIX; fid[1] = keyid; sc_path_set(&path, SC_PATH_TYPE_FILE_ID, fid, 2, 0, -1); sc_delete_file(card, &path); } wrapped_key.key_id = keyid; r = sc_card_ctl(card, SC_CARDCTL_SC_HSM_UNWRAP_KEY, (void *)&wrapped_key); 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; } 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; } if (r < 0) { fprintf(stderr, "sc_card_ctl(*, SC_CARDCTL_SC_HSM_UNWRAP_KEY, *) failed with %s\n", sc_strerror(r)); return -1; } if (prkd_len > 0) { r = update_ef(card, PRKD_PREFIX, keyid, force, prkd, prkd_len); if (r < 0) { fprintf(stderr, "Updating private key description failed with %s\n", sc_strerror(r)); return -1; } } if (cert_len > 0) { r = update_ef(card, EE_CERTIFICATE_PREFIX, keyid, force, cert, cert_len); if (r < 0) { fprintf(stderr, "Updating certificate failed with %s\n", sc_strerror(r)); return -1; } } printf("Key successfully imported\n"); return 0; } static int export_key(sc_card_t *card, int keyid, const char *outf) { sc_path_t path; FILE *outfp = NULL; u8 fid[2]; u8 ef_cert[MAX_CERT]; u8 dev_aut_cert[MAX_CERT]; u8 dica[MAX_CERT]; u8 tag = SC_ASN1_TAG_CONSTRUCTED | SC_ASN1_TAG_SEQUENCE; /* 0x30 */ int r = 0, ef_cert_len, total_certs_len; size_t dev_aut_cert_len, dica_len; 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, NULL); if (r != SC_SUCCESS) { fprintf(stderr, "Wrong key reference (-i %d)? Failed to select file: %s\n", keyid, sc_strerror(r)); return -1; } ef_cert_len = sc_read_binary(card, 0, ef_cert, sizeof(ef_cert), 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); } /* 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, NULL); if (r != SC_SUCCESS) { fprintf(stderr, "Failed to select certificates: %s\n", sc_strerror(r)); return -1; } total_certs_len = sc_read_binary(card, 0, dev_aut_cert, sizeof(dev_aut_cert), 0); if (total_certs_len < 0) { fprintf(stderr, "Error reading certificate: %s\n", sc_strerror(total_certs_len)); return -1; } else { dev_aut_cert_len = determineLength(dev_aut_cert, total_certs_len); dica_len = total_certs_len - dev_aut_cert_len; memcpy(dica, dev_aut_cert + dev_aut_cert_len, dica_len); } if (dica_len == 0) { fprintf(stderr, "Could not determine device issuer certificate\n"); return -1; } if ((outfp = fopen(outf, "r"))) { fprintf(stderr, "Output file '%s' already exists\n", outf); fclose(outfp); return -1; } 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; goto err; } err: if (outfp) fclose(outfp); if (out) free(out); if (data) free(data); 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; } 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; int do_initialize = 0; int do_import_dkek_share = 0; int do_print_dkek_share = 0; 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; int opt_password_shares_total = -1; int opt_force = 0; int opt_iter = 10000000; sc_context_param_t ctx_param; sc_context_t *ctx = NULL; sc_card_t *card = NULL; while (1) { 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 == '?') util_print_usage_and_die(app_name, options, option_help, NULL); switch (c) { case 'X': do_initialize = 1; action_count++; break; case 'C': do_create_dkek_share = 1; opt_filename = optarg; action_count++; break; case 'I': do_import_dkek_share = 1; opt_filename = optarg; action_count++; break; case 'P': do_print_dkek_share = 1; opt_filename = optarg; action_count++; break; case 'W': do_wrap_key = 1; opt_filename = optarg; action_count++; break; case 'U': do_unwrap_key = 1; 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; case OPT_SO_PIN: util_get_pin(optarg, &opt_so_pin); break; case OPT_PIN: util_get_pin(optarg, &opt_pin); break; case OPT_RETRY: 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 = (int)atol(optarg); break; case OPT_PASSWORD_SHARES_TOTAL: opt_password_shares_total = (int)atol(optarg); break; case 's': opt_dkek_shares = (int)atol(optarg); break; case 'f': opt_force = 1; break; case 'i': opt_key_reference = (int)atol(optarg); break; case 'r': opt_reader = optarg; break; case 'l': opt_label = optarg; break; case 'v': verbose++; break; case 'w': opt_wait = 1; break; } } 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) { fprintf(stderr, "Failed to establish context: %s\n", sc_strerror(r)); exit(1); } 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)); } goto end; } sc_path_set(&path, SC_PATH_TYPE_DF_NAME, sc_hsm_aid.value, sc_hsm_aid.len, 0, 0); r = sc_select_file(card, &path, &file); if (r != SC_SUCCESS) { fprintf(stderr, "Failed to select application: %s\n", sc_strerror(r)); goto fail; } 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)) goto fail; if (do_import_dkek_share && import_dkek_share(card, opt_filename, opt_iter, opt_password, opt_password_shares_total)) goto fail; 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(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); } err = 0; goto end; fail: err = 1; end: sc_disconnect_card(card); sc_release_context(ctx); ERR_print_errors_fp(stderr); return err; } OpenSC-0.26.1/src/tools/sceac-example.c000066400000000000000000000075271474147347300175620ustar00rootroot00000000000000/* * Copyright (C) 2011 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 */ /* This example shows how to use the library functions perform_pace to * get a secure channel to the nPA. We use the builtin function npa_change_pin * to modify the PIN using the secure channel. Then we transmit an arbitrary * APDU encrypted and authenticated to the card using sm_transmit_apdu. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #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; static const char *pin = NULL; /* SELECT the Master File (MF) */ const unsigned char apdubuf[] = {0x00, 0xA4, 0x00, 0x0C, 0x02, 0x3F, 0x00}; int main (int argc, char **argv) { /* Set up the environment */ int r; sc_context_t *ctx = NULL; sc_card_t *card = NULL; sc_reader_t *reader = NULL; sc_apdu_t apdu; u8 buf[0xffff]; 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); /* Connect to a reader */ r = sc_establish_context(&ctx, "example"); if (r < 0 || !ctx) { fprintf(stderr, "Failed to create initial context: %s", sc_strerror(r)); exit(1); } reader = sc_ctx_get_reader(ctx, 0); if (!reader) { fprintf(stderr, "Failed to access reader 0"); sc_release_context(ctx); exit(1); } /* Connect to a nPA */ ctx->flags |= SC_CTX_FLAG_ENABLE_DEFAULT_DRIVER; if (sc_connect_card(reader, &card) < 0) { fprintf(stderr, "Could not connect to card\n"); sc_release_context(ctx); exit(1); } /* 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 * asked for it. The same applies to the new PIN newpin. */ pace_input.pin_id = PACE_PIN; pace_input.pin = (unsigned char *) pin; pace_input.pin_length = pin ? strlen(pin) : 0; r = perform_pace(card, pace_input, &pace_output, EAC_TR_VERSION_2_02); if (r < 0) goto err; printf("Established PACE channel with PIN.\n"); r = npa_change_pin(card, newpin, newpin ? strlen(newpin) : 0); if (r < 0) goto err; printf("Changed PIN.\n"); /* Now we want to transmit additional APDUs in the established SM channel. * * Here we are parsing the raw apdu buffer apdubuf to be transformed into * an sc_apdu_t. Alternatively you could also set CLA, INS, P1, P2, ... by * hand in the sc_apdu_t object. */ r = sc_bytes2apdu(ctx, apdubuf, sizeof apdubuf, &apdu); if (r < 0) goto err; /* write the response data to buf */ apdu.resp = buf; apdu.resplen = sizeof buf; /* Transmit the APDU with SM */ r = sc_transmit_apdu(card, &apdu); err: fprintf(r < 0 ? stderr : stdout, "%s\n", sc_strerror(r)); /* Free up memory and wipe it if necessary (e.g. for keys stored in sm_ctx) */ 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); sc_sm_stop(card); sc_reset(card, 1); sc_disconnect_card(card); sc_release_context(ctx); return -r; } OpenSC-0.26.1/src/tools/util.c000066400000000000000000000264201474147347300160210ustar00rootroot00000000000000/* * util.c: utility functions used by OpenSC command line tools. * * Copyright (C) 2011 OpenSC Project developers * * 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 #ifndef _WIN32 #include #else #include #endif #include #include "util.h" #include "ui/notify.h" #include "common/compat_strlcat.h" int is_string_valid_atr(const char *atr_str) { unsigned char atr[SC_MAX_ATR_SIZE]; size_t atr_len = sizeof(atr); if (sc_hex_to_bin(atr_str, atr, &atr_len)) return 0; if (atr_len < 2) return 0; if (atr[0] != 0x3B && atr[0] != 0x3F) return 0; return 1; } int util_connect_reader (sc_context_t *ctx, sc_reader_t **reader, const char *reader_id, int do_wait) { struct sc_reader *found = NULL; int r; setbuf(stderr, NULL); setbuf(stdout, NULL); sc_notify_init(); if (do_wait) { 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|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 r; } r = sc_ctx_detect_readers(ctx); if (r < 0) { fprintf(stderr, "Error while refreshing readers: %s\n", sc_strerror(r)); return r; } } 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; } } else if (sc_ctx_get_reader_count(ctx) == 0) { fprintf(stderr, "No smart card readers found.\n"); 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); goto autofound; } } /* If no reader had a card, default to the first reader */ *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]; size_t atr_buf_len = sizeof(atr_buf); unsigned int i; sc_hex_to_bin(reader_id, atr_buf, &atr_buf_len); /* Loop readers, looking for a card with ATR */ for (i = 0; i < sc_ctx_get_reader_count(ctx); i++) { struct sc_reader *rdr = sc_ctx_get_reader(ctx, i); if (!(sc_detect_card_presence(rdr) & SC_READER_CARD_PRESENT)) continue; else if (rdr->atr.len != atr_buf_len) continue; else if (memcmp(rdr->atr.value, atr_buf, rdr->atr.len)) continue; fprintf(stderr, "Matched ATR in reader: %s\n", rdr->name); *reader = rdr; goto autofound; } } else { char *endptr = NULL; long num; errno = 0; num = strtol(reader_id, &endptr, 0); if (!errno && endptr && *endptr == '\0') *reader = sc_ctx_get_reader(ctx, (unsigned)num); else *reader = sc_ctx_get_reader_by_name(ctx, reader_id); } } autofound: if (!(*reader)) { fprintf(stderr, "Reader \"%s\" not found (%d reader(s) detected)\n", reader_id, sc_ctx_get_reader_count(ctx)); return SC_ERROR_READER; } if (sc_detect_card_presence(*reader) <= 0) { fprintf(stderr, "Card not present.\n"); 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; 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 r; } if (ctx->debug) printf("Using card driver %s.\n", card->driver->name); if (do_lock) { r = sc_lock(card); if (r < 0) { fprintf(stderr, "Failed to lock card: %s\n", sc_strerror(r)); sc_disconnect_card(card); return r; } } *cardp = card; return SC_SUCCESS; } int util_connect_card(sc_context_t *ctx, sc_card_t **cardp, const char *reader_id, int do_wait) { return util_connect_card_ex(ctx, cardp, reader_id, do_wait, 1); } void util_print_binary(FILE *f, const u8 *buf, size_t count) { size_t i; for (i = 0; i < count; i++) { unsigned char c = buf[i]; const char *format; if (!isprint(c)) format = "\\x%02X"; else format = "%c"; fprintf(f, format, c); } (void) fflush(f); } void util_hex_dump(FILE *f, const u8 *in, size_t len, const char *sep) { size_t i; for (i = 0; i < len; i++) { if (sep != NULL && i) fprintf(f, "%s", sep); fprintf(f, "%02X", in[i]); } } void util_hex_dump_asc(FILE *f, const u8 *in, size_t count, int addr) { int lines = 0; while (count) { char ascbuf[17]; size_t i; if (addr >= 0) { fprintf(f, "%08X: ", addr); addr += 16; } for (i = 0; i < count && i < 16; i++) { fprintf(f, "%02X ", *in); if (isprint(*in)) ascbuf[i] = *in; else ascbuf[i] = '.'; in++; } count -= i; ascbuf[i] = 0; for (; i < 16 && lines; i++) fprintf(f, " "); fprintf(f, "%s\n", ascbuf); lines++; } } void util_print_usage(const char *app_name, const struct option options[], const char *option_help[], const char *args) { int i; int header_shown = 0; if (args) printf("Usage: %s [OPTIONS] %s\n", app_name, args); else printf("Usage: %s [OPTIONS]\n", app_name); for (i = 0; options[i].name; i++) { char buf[40]; const char *arg_str; /* Skip "hidden" options */ if (option_help[i] == NULL) continue; if (!header_shown++) printf("Options:\n"); switch (options[i].has_arg) { case 1: arg_str = " "; break; case 2: arg_str = " [arg]"; break; default: arg_str = ""; break; } if (isascii(options[i].val) && isprint(options[i].val) && !isspace(options[i].val)) sprintf(buf, "-%c, --%s%s", options[i].val, options[i].name, arg_str); else sprintf(buf, " --%s%s", options[i].name, arg_str); /* print the line - wrap if necessary */ if (strlen(buf) > 28) { printf(" %s\n", buf); buf[0] = '\0'; } 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]; unsigned int acl; if (e == NULL) return "N/A"; line[0] = 0; while (e != NULL) { acl = e->method; switch (acl) { case SC_AC_UNKNOWN: return "N/A"; case SC_AC_NEVER: return "NEVR"; case SC_AC_NONE: return "NONE"; case SC_AC_CHV: strcpy(buf, "CHV"); if (e->key_ref != SC_AC_KEY_REF_NONE) sprintf(buf + 3, "%d", e->key_ref); break; case SC_AC_TERM: strcpy(buf, "TERM"); break; case SC_AC_PRO: strcpy(buf, "PROT"); break; case SC_AC_AUT: strcpy(buf, "AUTH"); if (e->key_ref != SC_AC_KEY_REF_NONE) sprintf(buf + 4, "%d", e->key_ref); break; case SC_AC_SEN: strcpy(buf, "Sec.Env. "); if (e->key_ref != SC_AC_KEY_REF_NONE) sprintf(buf + 3, "#%d", e->key_ref); break; case SC_AC_SCB: strcpy(buf, "Sec.ControlByte "); if (e->key_ref != SC_AC_KEY_REF_NONE) sprintf(buf + 3, "Ox%X", e->key_ref); break; case SC_AC_IDA: strcpy(buf, "PKCS#15 AuthID "); if (e->key_ref != SC_AC_KEY_REF_NONE) sprintf(buf + 3, "#%d", e->key_ref); break; default: strcpy(buf, "????"); break; } 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; } NORETURN void util_fatal(const char *fmt, ...) { va_list ap; va_start(ap, fmt); fprintf(stderr, "error: "); vfprintf(stderr, fmt, ap); fprintf(stderr, "\nAborting.\n"); va_end(ap); sc_notify_close(); exit(1); } void util_error(const char *fmt, ...) { va_list ap; va_start(ap, fmt); fprintf(stderr, "error: "); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); va_end(ap); } void util_warn(const char *fmt, ...) { va_list ap; va_start(ap, fmt); fprintf(stderr, "warning: "); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); va_end(ap); } int util_getpass (char **lineptr, size_t *len, FILE *stream) { #define MAX_PASS_SIZE 128 char *buf; size_t i; int ch = 0; #ifndef _WIN32 struct termios old, new; fflush(stdout); if (tcgetattr (fileno (stdout), &old) != 0) return -1; new = old; new.c_lflag &= ~ECHO; if (tcsetattr (fileno (stdout), TCSAFLUSH, &new) != 0) return -1; #endif buf = calloc(1, MAX_PASS_SIZE); if (!buf) return -1; for (i = 0; i < MAX_PASS_SIZE - 1; i++) { #ifndef _WIN32 ch = getchar(); #else ch = _getch(); #endif if (ch == 0 || ch == 3) break; if (ch == '\n' || ch == '\r') break; buf[i] = (char) ch; } #ifndef _WIN32 tcsetattr (fileno (stdout), TCSAFLUSH, &old); fputs("\n", stdout); #endif if (ch == 0 || ch == 3) { free(buf); return -1; } if (*lineptr && (!len || *len < i+1)) { free(*lineptr); *lineptr = NULL; } if (*lineptr) { memcpy(*lineptr,buf,i+1); memset(buf, 0, MAX_PASS_SIZE); free(buf); } else { *lineptr = buf; if (len) *len = MAX_PASS_SIZE; } return (int)i; } size_t util_get_pin(const char *input, const char **pin) { size_t inputlen = strlen(input); size_t pinlen = 0; 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 { //Just use the input *pin = input; pinlen = inputlen; } return pinlen; } OpenSC-0.26.1/src/tools/util.h000066400000000000000000000044651474147347300160330ustar00rootroot00000000000000#ifndef UTIL_H #define UTIL_H #include "config.h" #include #include #include #include #include #ifdef HAVE_STRINGS_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include #include #include "libopensc/opensc.h" #ifdef __cplusplus extern "C" { #endif #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(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, ...); 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 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); /* Get a PIN (technically just a string). The source depends on the value of *input: * env: - get from the environment variable * otherwise - use input */ size_t util_get_pin(const char *input, const char **pin); #ifdef __cplusplus } #endif #endif OpenSC-0.26.1/src/tools/versioninfo-opensc-notify.rc.in000066400000000000000000000027231474147347300227670ustar00rootroot00000000000000#include #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", "@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 Notify" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1200 END END OpenSC-0.26.1/src/tools/versioninfo-tools.rc.in000066400000000000000000000022411474147347300213250ustar00rootroot00000000000000#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 OpenSC-0.26.1/src/tools/westcos-tool.c000066400000000000000000000445221474147347300175110ustar00rootroot00000000000000/* * westcos-tool.c: tool 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include "libopensc/sc-ossl-compat.h" #include "libopensc/opensc.h" #include "libopensc/errors.h" #include "libopensc/pkcs15.h" #include "libopensc/cardctl.h" #include "util.h" static const char *app_name = "westcos-tool"; static const struct option options[] = { { "reader", 1, NULL, 'r' }, { "wait", 0, NULL, 'w' }, { "generate-key", 0, NULL, 'g' }, { "overwrite-key", 0, NULL, 'o' }, { "key-length", 1, NULL, 'l' }, { "install-pin", 0, NULL, 'i' }, { "pin-value", 1, NULL, 'x' }, { "puk-value", 1, NULL, 'y' }, { "change-pin", 0, NULL, 'n' }, { "unblock-pin", 0, NULL, 'u' }, { "certificate", 1, NULL, 't' }, { "finalize", 0, NULL, 'f' }, { "read-file", 1, NULL, 'j' }, { "write-file", 1, NULL, 'k' }, { "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", "Generate key 1536 default", "Overwrite key if already exist", "Key length [512,1024,1536]", "Install pin", "Pin value ", "Puk value ", "Change pin (new pin in puk value)", "Unblock pin", "Write certificate (in pem format)", "Finalize card(!!! MANDATORY FOR SECURITY !!!)", "Read file ", "Write file (ex 0002 write file 0002 to 0002)", "This message", "Verbose operation, may be used several times", }; static int opt_wait = 0, verbose = 0; static const char *opt_driver = NULL; static const char *opt_reader = NULL; static int finalize = 0; static int install_pin = 0; static int overwrite = 0; static char *cert = NULL; static int keylen = 0; static int new_pin = 0; static int unlock = 0; static char *get_filename = NULL; static char *put_filename = NULL; static int do_convert_bignum(sc_pkcs15_bignum_t *dst, const BIGNUM *src) { if (src == 0) return 0; dst->len = BN_num_bytes(src); dst->data = malloc(dst->len); if (!dst->data) return 0; if (!BN_bn2bin(src, dst->data)) return 0; return 1; } static void print_openssl_error(void) { long r; 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; 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 = pin_reference; data.flags = SC_PIN_CMD_NEED_PADDING; if (card->reader->capabilities & SC_READER_CAP_PIN_PAD) { printf("Please enter PIN on the reader's pin pad.\n"); data.pin1.prompt = "Please enter PIN"; data.flags |= SC_PIN_CMD_USE_PINPAD; } else { if(pin_value == NULL) { return SC_ERROR_INVALID_ARGUMENTS; } data.pin1.data = (u8*)pin_value; data.pin1.len = strlen(pin_value); } r = sc_pin_cmd(card, &data, &tries_left); if (r) { if (r == SC_ERROR_PIN_CODE_INCORRECT) { if (tries_left >= 0) printf("Error %d attempts left.\n", tries_left); else printf("Wrong pin.\n"); } else printf("The pin can be verify: %s\n", sc_strerror(r)); return -1; } printf("Pin correct.\n"); return 0; } static int change_pin(sc_card_t *card, int pin_reference, const char *pin_value1, const char *pin_value2) { int r, tries_left = -1; struct sc_pin_cmd_data data; memset(&data, 0, sizeof(data)); data.cmd = SC_PIN_CMD_CHANGE; data.pin_type = SC_AC_CHV; data.pin_reference = pin_reference; data.flags = SC_PIN_CMD_NEED_PADDING; if (card->reader->capabilities & SC_READER_CAP_PIN_PAD) { printf("Please enter PIN on the reader's pin pad.\n"); data.pin1.prompt = "Please enter PIN"; data.flags |= SC_PIN_CMD_USE_PINPAD; } else { if(pin_value1 == NULL || pin_value2 == NULL) { return SC_ERROR_INVALID_ARGUMENTS; } data.pin1.data = (u8*)pin_value1; data.pin1.len = strlen(pin_value1); data.pin2.data = (u8*)pin_value2; data.pin2.len = strlen(pin_value2); } r = sc_pin_cmd(card, &data, &tries_left); if (r) { if (r == SC_ERROR_PIN_CODE_INCORRECT) { if (tries_left >= 0) printf("Error %d attempts left.\n", tries_left); else printf("Wrong pin.\n"); } else printf("Can't change pin: %s\n", sc_strerror(r)); return -1; } printf("Pin changed.\n"); return 0; } static int unlock_pin(sc_card_t *card, int pin_reference, const char *puk_value, const char *pin_value) { int r, tries_left = -1; struct sc_pin_cmd_data data; memset(&data, 0, sizeof(data)); data.cmd = SC_PIN_CMD_UNBLOCK; data.pin_type = SC_AC_CHV; data.pin_reference = pin_reference; data.flags = SC_PIN_CMD_NEED_PADDING; if (card->reader->capabilities & SC_READER_CAP_PIN_PAD) { printf("Please enter PIN on the reader's pin pad.\n"); data.pin1.prompt = "Please enter PIN"; data.flags |= SC_PIN_CMD_USE_PINPAD; } else { if(pin_value == NULL || puk_value == NULL) { return SC_ERROR_INVALID_ARGUMENTS; } data.pin1.data = (u8*)puk_value; data.pin1.len = strlen(puk_value); data.pin2.data = (u8*)pin_value; data.pin2.len = strlen(pin_value); } r = sc_pin_cmd(card, &data, &tries_left); if (r) { if (r == SC_ERROR_PIN_CODE_INCORRECT) { if (tries_left >= 0) printf("Error %d attempts left.\n", tries_left); else printf("Wrong pin.\n"); } else printf("Can't unblock pin: %s\n", sc_strerror(r)); return -1; } printf("Unlock pin.\n"); return 0; } static int cert2der(X509 *in_cert, u8 **value) { int len; u8 *p; len = i2d_X509(in_cert, NULL); p = *value = malloc(len); i2d_X509(in_cert, &p); return len; } static int create_file_cert(sc_card_t *card) { int r; size_t size = 0; sc_path_t path; sc_file_t *file = NULL; sc_format_path("3F00", &path); r = sc_select_file(card, &path, &file); if(r) goto out; if(file) { size = (file->size) - 32; sc_file_free(file); file = NULL; } else { size = 2048; } sc_format_path("0002", &path); r = sc_select_file(card, &path, NULL); if(r) { if(r != SC_ERROR_FILE_NOT_FOUND) goto out; file = sc_file_new(); if(file == NULL) { printf("Memory error.\n"); goto out; } file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = SC_FILE_EF_TRANSPARENT; file->shareable = 0; file->size = size; r = sc_file_add_acl_entry(file, SC_AC_OP_READ, SC_AC_NONE, 0); if(r) goto out; r = sc_file_add_acl_entry(file, SC_AC_OP_UPDATE, SC_AC_CHV, 0); if(r) goto out; r = sc_file_add_acl_entry(file, SC_AC_OP_ERASE, SC_AC_CHV, 0); if(r) goto out; file->path = path; r = sc_create_file(card, file); if(r) goto out; } out: if(file) sc_file_free(file); return r; } int main(int argc, char *argv[]) { int r, c, long_optind = 0; sc_context_param_t ctx_param; sc_card_t *card = NULL; sc_context_t *ctx = NULL; sc_file_t *file = NULL; sc_path_t path; EVP_PKEY *pkey = NULL; BIGNUM *bn = NULL; BIO *mem = NULL; static const char *pin = NULL; static const char *puk = NULL; while (1) { c = getopt_long(argc, argv, "r:wgol:ix:y:nut:fj:k:hv", \ 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 'g': if(keylen == 0) keylen = 2048; break; case 'o': overwrite = 1; break; case 'l': keylen = atoi(optarg); break; case 'i': install_pin = 1; break; case 'x': util_get_pin(optarg, &pin); break; case 'y': util_get_pin(optarg, &puk); break; case 'n': new_pin = 1; break; case 'u': unlock = 1; break; case 't': cert = optarg; break; case 'f': finalize = 1; break; case 'j': get_filename = optarg; break; case 'k': put_filename = 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) { printf("Failed to establish context: %s\n", sc_strerror(r)); return 1; } if (opt_driver != NULL) { r = sc_set_card_driver(ctx, opt_driver); if (r) { printf("Driver '%s' not found!\n", opt_driver); goto out; } } r = util_connect_card(ctx, &card, opt_reader, opt_wait); if (r) goto out; sc_format_path("3F00", &path); r = sc_select_file(card, &path, NULL); if(r) goto out; if(install_pin) { sc_format_path("AAAA", &path); r = sc_select_file(card, &path, NULL); if(r) { if(r != SC_ERROR_FILE_NOT_FOUND) goto out; file = sc_file_new(); if(file == NULL) { printf("Not enough memory.\n"); goto out; } file->type = SC_FILE_TYPE_INTERNAL_EF; file->ef_structure = SC_FILE_EF_TRANSPARENT; file->shareable = 0; file->id = 0xAAAA; file->size = 37; r = sc_file_add_acl_entry(file, SC_AC_OP_READ, SC_AC_NONE, 0); if(r) goto out; r = sc_file_add_acl_entry(file, SC_AC_OP_UPDATE, SC_AC_NONE, 0); if(r) goto out; r = sc_file_add_acl_entry(file, SC_AC_OP_ERASE, SC_AC_NONE, 0); if(r) goto out; /* sc_format_path("3F00AAAA", &(file->path)); */ file->path = path; r = sc_create_file(card, file); if(r) goto out; } 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 = strlen(pin); pin_cmd.data = (u8*)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) goto out; ck.new_key.key_len = ret; r = sc_card_ctl(card, SC_CARDCTL_WESTCOS_CHANGE_KEY, &ck); if(r) goto out; } 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 = strlen(puk); puk_cmd.data = (u8*)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) goto out; ck.new_key.key_len = ret; r = sc_card_ctl(card, SC_CARDCTL_WESTCOS_CHANGE_KEY, &ck); if(r) goto out; } } if(new_pin) { if(change_pin(card, 0, pin, puk)) printf("Wrong pin.\n"); goto out; } if(unlock) { if(unlock_pin(card, 0, puk, pin)) printf("Error unblocking pin.\n"); goto out; } printf("verify pin.\n"); { if(verify_pin(card, 0, pin)) { printf("Wrong pin.\n"); goto out; } } if(keylen) { size_t lg; 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); bn = BN_new(); mem = BIO_new(BIO_s_mem()); pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL); if(bn == NULL || mem == NULL || pctx == NULL) { printf("Not enough memory.\n"); if (pctx) EVP_PKEY_CTX_free(pctx); goto out; } if (EVP_PKEY_keygen_init(pctx) != 1 || EVP_PKEY_CTX_set_rsa_keygen_bits(pctx, keylen) != 1 || EVP_PKEY_keygen(pctx, &pkey) != 1) { printf("Key generation failed.\n"); EVP_PKEY_CTX_free(pctx); goto out; } EVP_PKEY_CTX_free(pctx); if(!i2d_PrivateKey_bio(mem, pkey)) { printf("i2d_RSAPrivateKey_bio return %ld\n", ERR_get_error()); goto out; } lg = BIO_get_mem_data(mem, &pdata); sc_format_path("0001", &path); r = sc_select_file(card, &path, NULL); if(r) { if(r != SC_ERROR_FILE_NOT_FOUND) goto out; file = sc_file_new(); if(file == NULL) { printf("Not enough memory.\n"); goto out; } file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = SC_FILE_EF_TRANSPARENT; file->shareable = 0; file->size = ((lg/4)+1)*4; r = sc_file_add_acl_entry(file, SC_AC_OP_READ, SC_AC_CHV, 0); if(r) goto out; r = sc_file_add_acl_entry(file, SC_AC_OP_UPDATE, SC_AC_CHV, 0); if(r) goto out; r = sc_file_add_acl_entry(file, SC_AC_OP_ERASE, SC_AC_CHV, 0); if(r) goto out; file->path = path; printf("File key creation %s, size %"SC_FORMAT_LEN_SIZE_T"d.\n", file->path.value, file->size); r = sc_create_file(card, file); if(r) goto out; } else { if(!overwrite) { printf("Key file already exist,"\ " use -o to replace it.\n"); goto out; } } printf("Private key length is %"SC_FORMAT_LEN_SIZE_T"d\n", lg); printf("Write private key.\n"); r = sc_update_binary(card,0,pdata,lg,0); if(r<0) goto out; printf("Private key correctly written.\n"); r = create_file_cert(card); 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 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); sc_format_path("3F000002", &path); r = sc_select_file(card, &path, NULL); if(r) goto out; printf("Write public key.\n"); r = sc_update_binary(card,0,pdata,lg,0); if(r<0) goto out; printf("Public key correctly written.\n"); } if(cert) { BIO *bio; X509 *xp; u8 *pdata; bio = BIO_new(BIO_s_file()); if (BIO_read_filename(bio, cert) <= 0) { BIO_free(bio); printf("Can't open file %s.\n", cert); goto out; } xp = PEM_read_bio_X509(bio, NULL, NULL, NULL); BIO_free(bio); if (xp == NULL) { print_openssl_error(); goto out; } else { int lg = cert2der(xp, &pdata); sc_format_path("0002", &path); r = sc_select_file(card, &path, NULL); if(r) goto out; /* FIXME: verify if the file has a compatible size... */ printf("Write certificate %s.\n", cert); r = sc_update_binary(card,0,pdata,lg,0); if(r == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED) { if(verify_pin(card, 0, pin)) { printf("Wrong pin.\n"); } else { r = sc_update_binary(card,0,pdata,lg,0); } } if(r<0) { if(pdata) free(pdata); goto out; } if(xp) X509_free(xp); if(pdata) free(pdata); printf("Certificate correctly written.\n"); } } if(finalize) { int mode = SC_CARDCTRL_LIFECYCLE_USER; if(card->atr.value[10] != 0x82) { sc_format_path("0001", &path); r = sc_select_file(card, &path, NULL); if(r) { printf("This card don't have private key"\ " and can't be finalize.\n"); goto out; } printf("Finalize card...\n"); if(sc_card_ctl(card, SC_CARDCTL_WESTCOS_AUT_KEY, NULL) || sc_card_ctl(card, SC_CARDCTL_LIFECYCLE_SET, &mode)) { printf("Error finalizing card,"\ " card isn't secure.\n"); goto out; } } printf("Card correctly finalized.\n"); } if(get_filename) { FILE *fp; u8 *b; if(file) { sc_file_free(file); file = NULL; } sc_format_path(get_filename, &path); r = sc_select_file(card, &path, &file); if(r) { printf("Error file not found.\n"); goto out; } b = malloc(file->size); if(b == NULL) { printf("Not enough memory.\n"); goto out; } r = sc_read_binary(card, 0, b, file->size, 0); if(r == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED) { if(verify_pin(card, 0, pin)) { printf("Wrong pin.\n"); goto out; } r = sc_read_binary(card, 0, b, file->size, 0); } if(r<0) { printf("Error reading file.\n"); goto out; } fp = fopen(get_filename, "wb"); fwrite(b, 1, file->size, fp); fclose(fp); free(b); } if(put_filename) { FILE *fp; u8 *b; if(file) { sc_file_free(file); file = NULL; } sc_format_path(put_filename, &path); r = sc_select_file(card, &path, &file); if(r) { printf("File not found.\n"); goto out; } b = malloc(file->size); if(b == NULL) { printf("Not enough memory.\n"); goto out; } memset(b, 0, file->size); fp = fopen(put_filename, "rb"); 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); if(r == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED) { if(verify_pin(card, 0, pin)) { printf("Wrong pin.\n"); } else { r = sc_update_binary(card, 0, b, file->size, 0); } } if(r<0) { free(b); printf("Error writing file.\n"); goto out; } free(b); } out: if(mem) BIO_free(mem); if(bn) BN_free(bn); if(pkey) EVP_PKEY_free(pkey); if(file) sc_file_free(file); if (card) { sc_unlock(card); sc_disconnect_card(card); } sc_release_context(ctx); return EXIT_SUCCESS; } OpenSC-0.26.1/src/ui/000077500000000000000000000000001474147347300141515ustar00rootroot00000000000000OpenSC-0.26.1/src/ui/Makefile.am000066400000000000000000000007641474147347300162140ustar00rootroot00000000000000include $(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) OpenSC-0.26.1/src/ui/Makefile.mak000066400000000000000000000004631474147347300163630ustar00rootroot00000000000000TOPDIR = ..\.. 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) OpenSC-0.26.1/src/ui/char_str_from_wchar.h000066400000000000000000000025171474147347300203430ustar00rootroot00000000000000/* * 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 */ #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; } OpenSC-0.26.1/src/ui/invisible_window.h000066400000000000000000000030761474147347300177030ustar00rootroot00000000000000/* * 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; } OpenSC-0.26.1/src/ui/notify.c000066400000000000000000000340541474147347300156330ustar00rootroot00000000000000/* * 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 OpenSC-0.26.1/src/ui/notify.h000066400000000000000000000030571474147347300156370ustar00rootroot00000000000000/* * 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 OpenSC-0.26.1/src/ui/strings.c000066400000000000000000000204271474147347300160130ustar00rootroot00000000000000/* * 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 "libopensc/internal.h" #include "ui/strings.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 LANGID langid = GetUserDefaultUILanguage(); if ((langid & LANG_GERMAN) == LANG_GERMAN) { lang = DE; } #else /* LANGUAGE supersedes locale */ if (!find_lang_str(getenv("LANGUAGE"), &lang)) { /* XXX Should we use LC_MESSAGES instead? */ find_lang_str(setlocale(LC_ALL, ""), &lang); } #endif } /* 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; } OpenSC-0.26.1/src/ui/strings.h000066400000000000000000000031751474147347300160210ustar00rootroot00000000000000/* * 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 OpenSC-0.26.1/src/ui/wchar_from_char_str.h000066400000000000000000000024421474147347300203400ustar00rootroot00000000000000/* * 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; } OpenSC-0.26.1/tests/000077500000000000000000000000001474147347300141075ustar00rootroot00000000000000OpenSC-0.26.1/tests/Makefile.am000066400000000000000000000036421474147347300161500ustar00rootroot00000000000000MAINTAINERCLEANFILES = $(srcdir)/Makefile.in EXTRA_DIST = softhsm_ref.json softhsm_fips_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='/usr/lib/x86_64-linux-gnu/libpcsclite.so.1'; endif dist_noinst_SCRIPTS = common.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 .NOTPARALLEL: TESTS = \ test-manpage.sh \ test-duplicate-symbols.sh \ test-pkcs11-tool-test.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-sign-verify.sh \ test-pkcs11-tool-unwrap-wrap-test.sh \ test-pkcs11-tool-import.sh if ENABLE_TESTS if ENABLE_SHARED TESTS += test-p11test.sh endif endif endif XFAIL_TESTS = \ test-pkcs11-tool-test-threads.sh \ test-pkcs11-tool-test.sh OpenSC-0.26.1/tests/common.sh000066400000000000000000000054261474147347300157420ustar00rootroot00000000000000#!/bin/bash ## from OpenSC/src/tests/p11test/runtest.sh BUILD_PATH=${BUILD_PATH:-..} # run valgrind with all the switches we are interested in if [ -n "$VALGRIND" -a -n "$LOG_COMPILER" ]; then VALGRIND="$LOG_COMPILER" fi SOPIN="12345678" PIN="123456" PKCS11_TOOL="$VALGRIND $BUILD_PATH/src/tools/pkcs11-tool" 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 P11LIB=$LIB echo "Setting P11LIB=$LIB" break fi done if [[ -z "$P11LIB" ]]; then echo "Warning: Could not find the softhsm pkcs11 module" 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 --keypairgen --key-type="$TYPE" --login --pin=$PIN \ --module="$P11LIB" --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 --read-object --id $ID --type pubkey --output-file $ID.der \ --module="$P11LIB" 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 elif [[ $TYPE == "EC:edwards25519" ]]; then openssl pkey -inform DER -outform PEM -in $ID.der -pubin > $ID.pub else openssl ec -inform DER -outform PEM -in $ID.der -pubin > $ID.pub fi rm $ID.der } function softhsm_initialize() { echo "directories.tokendir = $(realpath .tokens)" > .softhsm2.conf if [ -d ".tokens" ]; then rm -rf ".tokens" fi mkdir ".tokens" export SOFTHSM2_CONF=$(realpath ".softhsm2.conf") # Init token softhsm2-util --init-token --slot 0 --label "SC test" --so-pin="$SOPIN" --pin="$PIN" } function card_setup() { softhsm_initialize # Generate 1024b RSA Key pair generate_key "RSA:1024" "01" "RSA_auth" || return 1 # Generate 2048b RSA Key pair generate_key "RSA:2048" "02" "RSA2048" || 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 --keygen --key-type="GENERIC:64" --login --pin=$PIN \ --module="$P11LIB" --label="HMAC-SHA256" --id="05" if [[ "$?" -ne "0" ]]; then echo "Couldn't generate GENERIC key" return 1 fi } function softhsm_cleanup() { rm .softhsm2.conf rm -rf ".tokens" } function card_cleanup() { softhsm_cleanup rm 0{1,2,3,4}.pub } OpenSC-0.26.1/tests/opensc.supp000066400000000000000000000034751474147347300163200ustar00rootroot00000000000000{ 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-0.26.1/tests/softhsm_fips_ref.json000066400000000000000000000310741474147347300203470ustar00rootroot00000000000000{ "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", "DERIVE PUBLIC", "DERIVE PRIVATE", "ALWAYS AUTH" ], [ "01", "RSA_auth", "RSA", "1024", "", "YES", "YES", "YES", "YES", "YES", "YES", "", "", "" ], [ "02", "RSA2048", "RSA", "2048", "", "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", "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", "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" }] } OpenSC-0.26.1/tests/softhsm_ref.json000066400000000000000000000316461474147347300173330ustar00rootroot00000000000000{ "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", "DERIVE PUBLIC", "DERIVE PRIVATE", "ALWAYS AUTH" ], [ "01", "RSA_auth", "RSA", "1024", "", "YES", "YES", "YES", "YES", "YES", "YES", "", "", "" ], [ "02", "RSA2048", "RSA", "2048", "", "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", "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", "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" }] } OpenSC-0.26.1/tests/test-duplicate-symbols.sh000077500000000000000000000006151474147347300210650ustar00rootroot00000000000000#!/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 OpenSC-0.26.1/tests/test-fuzzing.sh000077500000000000000000000021341474147347300171170ustar00rootroot00000000000000#!/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 OpenSC-0.26.1/tests/test-manpage.sh000077500000000000000000000020041474147347300170270ustar00rootroot00000000000000#!/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 OpenSC-0.26.1/tests/test-p11test.sh000077500000000000000000000037271474147347300167350ustar00rootroot00000000000000#!/bin/bash SOURCE_PATH=${SOURCE_PATH:-..} source $SOURCE_PATH/tests/common.sh echo "=======================================================" echo "Setup SoftHSM" echo "=======================================================" if [[ ! -f $P11LIB ]]; then echo "WARNING: The SoftHSM is not installed. Can not run this test" exit 77; fi # The Ubuntu has old softhsm version not supporting this feature grep "Ubuntu 18.04" /etc/issue && echo "WARNING: Not supported on Ubuntu 18.04" && exit 77 card_setup assert $? "Failed to set up card" echo "=======================================================" echo "Run p11test" echo "=======================================================" $VALGRIND ./../src/tests/p11test/p11test -v -m $P11LIB -o softhsm.json -p $PIN assert $? "Failed running tests" # Run the input shrough sed to skip the mechanism part: # * broken because of uninitialized memory in softhsm # * different for different softhsm versions # and interface tests # * different results for softhsm and pkcs11-spy function filter_log() { sed -n '/readonly_tests/,$p' $1 } REF_FILE="$SOURCE_PATH/tests/softhsm_ref.json" if [[ -f "/proc/sys/crypto/fips_enabled" && $(cat /proc/sys/crypto/fips_enabled) == "1" ]]; then REF_FILE="$SOURCE_PATH/tests/softhsm_fips_ref.json" fi diff -U3 <(filter_log $REF_FILE) <(filter_log softhsm.json) assert $? "Unexpected results" echo "=======================================================" echo "Run p11test with PKCS11SPY" echo "=======================================================" export PKCS11SPY="$P11LIB" $VALGRIND ./../src/tests/p11test/p11test -v -m ../src/pkcs11/.libs/pkcs11-spy.so -o softhsm.json -p $PIN assert $? "Failed running tests" diff -U3 <(filter_log $REF_FILE) <(filter_log softhsm.json) assert $? "Unexpected results with PKCS11 spy" rm softhsm.json echo "=======================================================" echo "Cleanup" echo "=======================================================" card_cleanup exit $ERRORS OpenSC-0.26.1/tests/test-pkcs11-tool-allowed-mechanisms.sh000077500000000000000000000035341474147347300232570ustar00rootroot00000000000000#!/bin/bash SOURCE_PATH=${SOURCE_PATH:-..} source $SOURCE_PATH/tests/common.sh echo "=======================================================" echo "Setup SoftHSM" echo "=======================================================" if [[ ! -f $P11LIB ]]; then echo "WARNING: The SoftHSM is not installed. Can not run this test" exit 77; fi # The Ubuntu has old softhsm version not supporting this feature grep "Ubuntu 18.04" /etc/issue && echo "WARNING: Not supported on Ubuntu 18.04" && exit 77 softhsm_initialize 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 "=======================================================" softhsm_cleanup rm objects.list sign.log exit $ERRORS OpenSC-0.26.1/tests/test-pkcs11-tool-import.sh000077500000000000000000000032741474147347300210160ustar00rootroot00000000000000#!/bin/bash SOURCE_PATH=${SOURCE_PATH:-..} source $SOURCE_PATH/tests/common.sh echo "=======================================================" echo "Setup SoftHSM" 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" for KEYTYPE in "RSA" "EC"; do echo "=======================================================" echo "Generate and import $KEYTYPE keys" echo "=======================================================" ID="0100" OPTS="" if [ "$KEYTYPE" == "EC" ]; then ID="0200" OPTS="-pkeyopt ec_paramgen_curve:P-521" fi openssl genpkey -out "${KEYTYPE}_private.der" -outform DER -algorithm $KEYTYPE $OPTS assert $? "Failed to generate private $KEYTYPE key" $PKCS11_TOOL --write-object "${KEYTYPE}_private.der" --id "$ID" --type privkey \ --label "$KEYTYPE" -p "$PIN" --module "$P11LIB" assert $? "Failed to write private $KEYTYPE key" 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 --write-object "${KEYTYPE}_public.der" --id "$ID" --type pubkey --label "$KEYTYPE" \ -p $PIN --module $P11LIB assert $? "Failed to write public $KEYTYPE key" # certificate import already tested in all other tests rm "${KEYTYPE}_private.der" "${KEYTYPE}_public.der" done echo "=======================================================" echo "Cleanup" echo "=======================================================" card_cleanup exit $ERRORS OpenSC-0.26.1/tests/test-pkcs11-tool-sign-verify.sh000077500000000000000000000237311474147347300217460ustar00rootroot00000000000000#!/bin/bash SOURCE_PATH=${SOURCE_PATH:-..} source $SOURCE_PATH/tests/common.sh echo "=======================================================" echo "Setup SoftHSM" 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" # get informaation 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 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 --id $SIGN_KEY -s -p $PIN -m $METHOD --module $P11LIB \ --input-file data --output-file data.sig assert $? "Failed to Sign data" # OpenSSL verification 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 $PKCS11_TOOL --id $SIGN_KEY --verify -m $METHOD --module $P11LIB \ --input-file data --signature-file data.sig assert $? "Failed to Verify signature using pkcs11-tool" rm data.sig METHOD="$METHOD-PSS" # -PSS methods should work on data > 512 bytes; generate data: head -c 1024 data if [[ "$HASH" == "SHA512" ]]; then continue; # This one is broken fi # Ubuntu SoftHSM version does not support RSA-PSS grep "Ubuntu 18.04" /etc/issue && echo "WARNING: Not supported on Ubuntu 18.04" && continue 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 --id $SIGN_KEY -s -p $PIN -m $METHOD --module $P11LIB \ $HASH_ALGORITM --salt-len=-1 \ --input-file data.hash --output-file data.sig assert $? "Failed to Sign data" # OpenSSL verification 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 $PKCS11_TOOL --id $SIGN_KEY --verify -m $METHOD --module $P11LIB \ $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} done 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 # Skip hashed algorithms (do not support encryption & decryption) if [[ ! -z "$HASH" ]]; 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 --id $ENC_KEY --decrypt -p $PIN -m $METHOD \ --module $P11LIB --input-file data.crypt > data.decrypted diff data{,.decrypted} assert $? "The decrypted data do not match the original" rm data.{crypt,decrypted} # TODO pkcs11-tool encryption not supported 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 --id $SIGN_KEY -s -p $PIN -m $METHOD --module $P11LIB \ --input-file data.hash --output-file data.sig assert $? "Failed to Sign data" $PKCS11_TOOL --id $SIGN_KEY -s -p $PIN -m $METHOD --module $P11LIB \ --input-file data.hash --output-file data.sig.openssl \ --signature-format openssl assert $? "Failed to Sign data into OpenSSL format" # OpenSSL verification openssl dgst -keyform PEM -verify $SIGN_KEY.pub -sha256 \ -signature data.sig.openssl data assert $? "Failed to Verify signature using OpenSSL" # pkcs11-tool verification $PKCS11_TOOL --id $SIGN_KEY --verify -m $METHOD --module $P11LIB \ --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 --login --pin=$PIN --sign --mechanism=$MECHANISM \ --input-file=data.msg --output-file=data.sig --module $P11LIB assert $? "Failed to Sign data" $PKCS11_TOOL --login --pin=$PIN --verify --mechanism=$MECHANISM \ --input-file=data.msg --signature-file=data.sig --module $P11LIB 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 OpenSC-0.26.1/tests/test-pkcs11-tool-sym-crypt-test.sh000077500000000000000000000235241474147347300224300ustar00rootroot00000000000000#!/bin/bash source common.sh echo "=======================================================" echo "Setup SoftHSM" echo "=======================================================" if [[ ! -f $P11LIB ]]; then echo "WARNING: The SoftHSM is not installed. Can not run this test" exit 77; fi # The Ubuntu has old softhsm version not supporting this feature grep "Ubuntu 18.04" /etc/issue && echo "WARNING: Not supported on Ubuntu 18.04" && exit 77 softhsm_initialize #echo "=======================================================" #echo "Generate AES key" #echo "=======================================================" #ID1="85" # Generate key #$PKCS11_TOOL --keygen --key-type="aes:32" --login --pin=$PIN \ # --module="$P11LIB" --label="gen_aes256" --id="$ID1" #assert $? "Failed to Generate AES key" echo "=======================================================" echo "import AES key" echo "=======================================================" ID2="86" echo -n "pppppppppppppppp" > aes_128.key # import key softhsm2-util --import aes_128.key --aes --token "SC test" --pin "$PIN" --label import_aes_128 --id "$ID2" assert $? "Fail, unable to import key" $PKCS11_TOOL --module="$P11LIB" --list-objects -l --pin=$PIN 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 --module="$P11LIB" --pin "$PIN" --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 --module="$P11LIB" --pin "$PIN" --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 --module="$P11LIB" --pin "$PIN" --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 --module="$P11LIB" --pin "$PIN" --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 --module="$P11LIB" --pin "$PIN" --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 --module="$P11LIB" --pin "$PIN" --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 --module="$P11LIB" --pin "$PIN" --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 --module="$P11LIB" --pin "$PIN" --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 --module="$P11LIB" --pin "$PIN" --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 --module="$P11LIB" --pin "$PIN" --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 --module="$P11LIB" --pin "$PIN" --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 --module="$P11LIB" --pin "$PIN" --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 softhsm2-util --import gcm_128.key --aes --token "SC test" --pin "$PIN" --label import_aes_gcm_128 --id "$ID3" >/dev/null assert $? "Fail, unable to import key" $PKCS11_TOOL --module="$P11LIB" --pin "$PIN" --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 --module="$P11LIB" --pin "$PIN" --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 "Cleanup" echo "=======================================================" softhsm_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 OpenSC-0.26.1/tests/test-pkcs11-tool-test-threads.sh000077500000000000000000000012421474147347300221040ustar00rootroot00000000000000#!/bin/bash SOURCE_PATH=${SOURCE_PATH:-..} source $SOURCE_PATH/tests/common.sh # Test our PKCS #11 module here P11LIB="../src/pkcs11/.libs/opensc-pkcs11.so" echo "=======================================================" echo "Test pkcs11 threads IN " echo "=======================================================" $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 OpenSC-0.26.1/tests/test-pkcs11-tool-test.sh000077500000000000000000000034071474147347300204610ustar00rootroot00000000000000#!/bin/bash SOURCE_PATH=${SOURCE_PATH:-..} source $SOURCE_PATH/tests/common.sh echo "=======================================================" echo "Setup SoftHSM" echo "=======================================================" if [[ ! -f $P11LIB ]]; then echo "WARNING: The SoftHSM is not installed. Can not run this test" exit 77; fi # The Ubuntu has old softhsm version not supporting this feature grep "Ubuntu 18.04" /etc/issue && echo "WARNING: Not supported on Ubuntu 18.04" && exit 77 card_setup assert $? "Failed to set up card" echo "=======================================================" echo "Test" echo "=======================================================" $PKCS11_TOOL --test -p "${PIN}" --module "${P11LIB}" assert $? "Failed running tests" echo "=======================================================" echo "Test objects URI" echo "=======================================================" $PKCS11_TOOL -O 2>/dev/null | grep 'uri:' 2>/dev/null >/dev/null assert $? "Failed running objects URI tests" $PKCS11_TOOL -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 -L 2>/dev/null | grep 'uri' 2>/dev/null >/dev/null assert $? "Failed running slots URI tests" $PKCS11_TOOL -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}" OpenSC-0.26.1/tests/test-pkcs11-tool-unwrap-wrap-test.sh000077500000000000000000000266421474147347300227500ustar00rootroot00000000000000#!/bin/bash source common.sh echo "=======================================================" echo "Setup SoftHSM" echo "=======================================================" if [[ ! -f $P11LIB ]]; then echo "WARNING: The SoftHSM is not installed. Can not run this test" exit 77; fi # The Ubuntu has old softhsm version not supporting this feature grep "Ubuntu 18.04" /etc/issue && echo "WARNING: Not supported on Ubuntu 18.04" && exit 77 softhsm_initialize PKCS11_TOOL_W_PIN="$PKCS11_TOOL --module $P11LIB --pin $PIN" echo "=======================================================" echo " RSA-PKCS Unwrap test" echo "=======================================================" ID1="85" ID2="95" ID3="96" # Generate RSA key (this key is used to unwrap/wrap operation) $PKCS11_TOOL_W_PIN --keypairgen --key-type rsa:1024 --id $ID1 --usage-wrap assert $? "Failed to Generate RSA key" # export public key $PKCS11_TOOL_W_PIN --read-object --type pubkey --id $ID1 -o rsa_pub.key assert $? "Failed to export public key" # create AES key KEY="70707070707070707070707070707070" echo -n $KEY|xxd -p -r > aes_plain_key # wrap AES key openssl rsautl -encrypt -pubin -keyform der -inkey rsa_pub.key -in aes_plain_key -out aes_wrapped_key assert $? "Failed wrap AES key" # unwrap key by pkcs11 interface $PKCS11_TOOL_W_PIN --unwrap -m RSA-PKCS --id $ID1 -i aes_wrapped_key --key-type GENERIC: \ --extractable --application-id $ID3 --application-label "unwrap-generic-ex" 2>/dev/null assert $? "Unwrap failed" # because key is extractable, there is no problem to compare key value with original key $PKCS11_TOOL_W_PIN --id $ID3 --read-object --type secrkey --output-file generic_extracted_key assert $? "unable to read key value" cmp generic_extracted_key aes_plain_key >/dev/null 2>/dev/null assert $? "extracted key does not match the input key" # unwrap AES key, not extractable $PKCS11_TOOL_W_PIN --unwrap -m RSA-PKCS --id $ID1 -i aes_wrapped_key --key-type AES: \ --application-id $ID2 --application-label "unwrap-aes" 2>/dev/null assert $? "Unwrap failed" # To check if AES key was correctly unwrapped (non extractable), we need to encrypt some data by pkcs11 interface and by openssl # (with same key). If result is same, key was correctly unwrapped. VECTOR="00000000000000000000000000000000" echo -n "UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU" > aes_plain.data openssl enc -aes-128-cbc -nopad -in aes_plain.data -out aes_ciphertext_openssl.data -iv $VECTOR -K $KEY assert $? "Fail/Openssl" $PKCS11_TOOL_W_PIN --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" echo "=======================================================" echo " RSA-PKCS Wrap test" echo "=======================================================" $PKCS11_TOOL_W_PIN --wrap -m RSA-PKCS --id $ID1 --application-id $ID3 --output-file wrapped.key assert $? "Fail, unable to wrap" $PKCS11_TOOL_W_PIN --decrypt -m RSA-PKCS --id $ID1 --input-file wrapped.key --output-file plain_wrapped.key assert $? "Fail, unable to decrypt wrapped key" cmp plain_wrapped.key aes_plain_key >/dev/null 2>/dev/null assert $? "wrapped key after decipher does not match the original key" echo "=======================================================" echo " RSA-PKCS Cleanup" echo "=======================================================" rm rsa_pub.key aes_plain_key aes_wrapped_key aes_ciphertext_pkcs11.data aes_ciphertext_openssl.data aes_plain.data generic_extracted_key wrapped.key plain_wrapped.key echo "=======================================================" echo " AES wrap/unwrap" echo "=======================================================" ID_RSA=$(echo "RSA" | tr -d "\n" | od -An -vtx1 | tr -d " " | tr -d "\n") ID_AES=$(echo "AES" | tr -d "\n" | od -An -vtx1 | tr -d " " | tr -d "\n") ID_KEK=$(echo "KEK" | tr -d "\n" | od -An -vtx1 | tr -d " " | tr -d "\n") ID_UNWRAPPED=$(echo "UW" | tr -d "\n" | od -An -vtx1 | tr -d " " | tr -d "\n") openssl genpkey -algorithm RSA -out rsa_priv.pem assert $? "OpenSSL / Failed to generate RSA private key" openssl pkey -in rsa_priv.pem -pubout -out rsa_pub.pem assert $? "OpenSSL / Failed to convert RSA private key to public" openssl pkcs8 -topk8 -inform PEM -outform DER -in rsa_priv.pem -out rsa_priv.der -nocrypt assert $? "OpenSSL / Failed to PKCS8 encode RSA private key" $PKCS11_TOOL_W_PIN --write-object rsa_priv.pem --id $ID_RSA --type privkey --usage-sign --extractable assert $? "PKCS11 / Failed to write RSA private key" $PKCS11_TOOL_W_PIN --write-object rsa_pub.pem --id $ID_RSA --type pubkey --usage-sign assert $? "PKCS11 / Failed to write RSA public key" AES_KEY=$(head /dev/urandom | sha256sum | head -c 64) echo -n $AES_KEY | xxd -p -r > aes.key $PKCS11_TOOL_W_PIN --write-object aes.key --id $ID_AES --type secrkey --key-type AES:32 --usage-decrypt --extractable assert $? "PKCS11 / Failed to write AES key" AES_KEK=$(head /dev/urandom | sha256sum | head -c 64) echo -n $AES_KEK | xxd -p -r > aes_kek.key $PKCS11_TOOL_W_PIN --write-object aes_kek.key --id $ID_KEK --type secrkey --key-type AES:32 --usage-wrap --extractable assert $? "PKCS11 / Failed to write AES KEK" is_openssl_3=$(openssl version | grep "OpenSSL 3.") is_softhsm2_2_6_1=$(softhsm2-util -version | grep "2.6.1") if [[ -n $is_softhsm2_2_6_1 ]] then # CKM_AES_CBC -- SoftHSM2 AES CBC wrapping currently has a bug, the IV is not correctly used. Only IV=0 will work --* IV="00000000000000000000000000000000" # RSA key # SoftHSM2 does not support wrapping asymmetric keys with AES CBC since the length of the encoded private key is likely not aligned to 16 bytes and SoftHSM2 does not pad the input as intended in the PKCS#11 documentation (PKCS#11 mechanisms v3.0, section 2.10.5) # AES Key openssl enc -aes-256-cbc -e -K $AES_KEK -iv $IV -in aes.key -out openssl_wrapped.data -nopad assert $? "OpenSSL / Failed to AES CBC encrypt AES key" # Wrapping $PKCS11_TOOL_W_PIN --wrap -m AES-CBC --id $ID_KEK --iv $IV --application-id $ID_AES --output-file pkcs11_wrapped.data assert $? "PKCS11 / Failed to AES CBC wrap AES key" cmp pkcs11_wrapped.data openssl_wrapped.data 2>&1 >/dev/null assert $? "AES CBC wrong AES key wrap" # Unwrapping -- SoftHSM2 currently does not support CKM_AES_CBC unwrapping -- # $PKCS11_TOOL_W_PIN --unwrap -m AES-CBC --id $ID_KEK --iv $IV --application-id $ID_UNWRAPPED --key-type AES: --input-file openssl_wrapped.data --extractable # assert $? "PKCS11 / Failed to AES CBC unwrap AES key" # $PKCS11_TOOL_W_PIN --read-object --type secrkey --id $ID_UNWRAPPED --output-file unwrapped.key # assert $? "PKCS11 / Failed to read unwrapped AES key" # cmp aes.key unwrapped.key # assert $? "AES CBC wrong AES key unwrap" # Cleanup # $PKCS11_TOOL_W_PIN --delete-object --type secrkey --id $ID_UNWRAPPED # assert $? "PKCS11 / Failed to delete unwrapped AES key" fi # CKM_AES_CBC_PAD -- SoftHSM2 currently doesn't support CKM_AES_CBC_PAD as a wrapping mechanism -- # IV="000102030405060708090A0B0C0D0E0F" # RSA key # $PKCS11_TOOL_W_PIN --wrap -m AES-CBC-PAD --id $ID_KEK --iv $IV --application-id $ID_RSA --output-file pkcs11_wrapped.data # assert $? "PKCS11 / Failed to AES CBC PAD wrap RSA priv key" # openssl enc -aes-256-cbc -e -K $AES_KEK -iv $IV -in rsa_priv.der -out openssl_wrapped.data # assert $? "OpenSSL / Failed to AES CBC encrypt RSA priv key" # cmp pkcs11_wrapped.data openssl_wrapped.data 2>&1 >/dev/null # assert $? "AES CBC PAD wrong RSA key wrap" # AES key # $PKCS11_TOOL_W_PIN --wrap -m AES-CBC-PAD --id $ID_KEK --iv $IV --application-id $ID_AES --output-file pkcs11_wrapped.data # assert $? "PKCS11 / Failed to AES CBC PAD wrap AES key" # openssl enc -aes-256-cbc -e -K $AES_KEK -iv $IV -in aes.key -out openssl_wrapped.data # assert $? "OpenSSL / Failed to AES CBC encrypt AES key" # cmp pkcs11_wrapped.data openssl_wrapped.data 2>&1 >/dev/null # assert $? "AES CBC PAD wrong AES key wrap" if [[ -n $is_openssl_3 ]] then # CKM_AES_KEY_WRAP IV="a6a6a6a6a6a6a6a6" # 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 # AES Key openssl enc -id-aes256-wrap -e -K $AES_KEK -iv $IV -in aes.key -out openssl_wrapped.data assert $? "OpenSSL / Failed to AES KEY WRAP wrap AES key" # Wrapping $PKCS11_TOOL_W_PIN --wrap -m AES-KEY-WRAP --id $ID_KEK --iv $IV --application-id $ID_AES --output-file pkcs11_wrapped.data assert $? "PKCS11 / Failed to AES KEY WRAP wrap AES key" cmp pkcs11_wrapped.data openssl_wrapped.data 2>&1 >/dev/null assert $? "AES KEY WRAP wrong AES key wrap" # Unwrapping $PKCS11_TOOL_W_PIN --unwrap -m AES-KEY-WRAP --id $ID_KEK --iv $IV --application-id $ID_UNWRAPPED --key-type AES: --input-file openssl_wrapped.data --extractable assert $? "PKCS11 / Failed to AES KEY WRAP wrap unwrap AES key" $PKCS11_TOOL_W_PIN --read-object --type secrkey --id $ID_UNWRAPPED --output-file unwrapped.key assert $? "PKCS11 / Failed to read unwrapped AES key" cmp aes.key unwrapped.key assert $? "AES KEY WRAP wrong AES key unwrap" # Cleanup $PKCS11_TOOL_W_PIN --delete-object --type secrkey --id $ID_UNWRAPPED assert $? "PKCS11 / Failed to delete unwrapped AES key" # CKM_AES_KEY_WRAP_PAD IV="a65959a6" # RSA Key -- Fails with the current version of SoftHSM2 -- # $PKCS11_TOOL_W_PIN --wrap -m AES-KEY-WRAP-PAD --id $ID_KEK --iv $IV --application-id $ID_RSA --output-file pkcs11_wrapped.data # assert $? "PKCS11 / Failed to AES KEY WRAP PAD wrap RSA priv key" # openssl enc -id-aes256-wrap-pad -e -K $AES_KEK -iv $IV -in rsa_priv.der -out openssl_wrapped.data # assert $? "OpenSSL / Failed to AES KEY WRAP PAD encrypt RSA priv key" # cmp pkcs11_wrapped.data openssl_wrapped.data 2>&1 >/dev/null # assert $? "AES KEY WRAP PAD wrong RSA key wrap" # AES Key openssl enc -id-aes256-wrap-pad -e -K $AES_KEK -iv $IV -in aes.key -out openssl_wrapped.data assert $? "OpenSSL / Failed to AES KEY WRAP PAD encrypt AES key" # Wrapping $PKCS11_TOOL_W_PIN --wrap -m AES-KEY-WRAP-PAD --id $ID_KEK --iv $IV --application-id $ID_AES --output-file pkcs11_wrapped.data assert $? "PKCS11 / Failed to AES KEY WRAP PAD wrap AES key" cmp pkcs11_wrapped.data openssl_wrapped.data 2>&1 >/dev/null assert $? "AES KEY WRAP PAD wrong AES key wrap" # Unwrapping $PKCS11_TOOL_W_PIN --unwrap -m AES-KEY-WRAP-PAD --id $ID_KEK --iv $IV --application-id $ID_UNWRAPPED --key-type AES: --input-file openssl_wrapped.data --extractable assert $? "PKCS11 / Failed to AES KEY WRAP PAD wrap unwrap AES key" $PKCS11_TOOL_W_PIN --read-object --type secrkey --id $ID_UNWRAPPED --output-file unwrapped.key assert $? "PKCS11 / Failed to read unwrapped AES key" cmp aes.key unwrapped.key assert $? "AES KEY WRAP PAD wrong AES key unwrap" # Cleanup $PKCS11_TOOL_W_PIN --delete-object --type secrkey --id $ID_UNWRAPPED assert $? "PKCS11 / Failed to delete unwrapped AES key" fi rm rsa_priv.pem rsa_pub.pem rsa_priv.der aes.key aes_kek.key pkcs11_wrapped.data openssl_wrapped.data unwrapped.key echo "=======================================================" echo "Cleanup" echo "=======================================================" softhsm_cleanup exit $ERRORS OpenSC-0.26.1/win32/000077500000000000000000000000001474147347300137075ustar00rootroot00000000000000OpenSC-0.26.1/win32/DDORes.dll_14_2302.ico000066400000000000000000001534451474147347300172230ustar00rootroot00000000000000 00h >& ( 006 $hN+ sa000 %)  ѷ y h(0`xx8{8{xyw;{7{{xpxxppxxpw( @wx{{{;{;{{{{{{;x{{7xxpxxx?????????????????(0 {wwwxxp( xxxxxxxxxw{xxxxxwxxwxx{xxxwwwww(0` >Sw9Zu:*6=zHUIRX^rdkdlqv}ôŻɽ",AH@"&-CHYjjA"")@GTdjjddjA"",AHddjjddddgjA++(>DTjjddddddgdddjB++%,CQdghddddgdgdddgddjB++)-AHddjddddgdgdgH?''PdjgdC+++AGHZhhddg[g[gTdF+% EgS#?gdjC@@DHRTdjhddg[[[[TF<%$I\j/jj_g ddjC>RTYddddddgdTdS< .8M/O_OL.LNONdgjB@TYYYZdgTdgYg?3M_O; 4656:;LLddhB@TYTYYTdgTd[TNO9 459;LLdgdD@TYYYYZTZZTd?/;  445/.0dgdB@TTYTYTYTTZT'/  2:;dZjB>TYTYRYYTTTT'/456656ZddF>YYYYRYRRRRR'/ 44ZTdF>YfdYRYQRQQQ%$2654  454ZgdF>YdfhhYQRQQQ%1;6YTdF>YfhjjjjdHHH%1;::964ZZdF>[fhjjjjjjjd%1;;/  jjdF>Yhjjjjjjjjj[hjjjjjjjjj(,ZhjjjjjjjjjjjjjjjjjdTQDA,(!,ZjjjjjjjjjjjjdRDC>)(,ZjjjjjjZQD@,("+ZYQGA,("@)!( @>StSw9W\u:*8=1=zCJRgkiv`~ôüżɽ++0C++.APbbW---AMabjgbjN00+?H\bjjbbbgbbP+++1GZbjbgbbW<ggN++0GZbgbjbbN*8;963)gN+a\\aa_a(  5)bL+a\\\\\_( )bM+fa\\\\\$(_M+fjj\\PZ$7 (aH+gfjjj\Z$;7(_H+jjjjjjj*42 (jW+gjjjjjj*UT386NgW+jgjjjjj*UU4;8 hhW+hhhjjjjN'S''(@Zjjjhg_+hhhjhjjjF#C>>CC *9$C<<>>  9$M>9<>+ 7$MMMC9,( 9$MMMHM') !?$MMMMMHI'+!M?$MHIHM0!04CMMMM?#MMHMHMMMMMME?7/"$MHMMMEC6/"#C91"$( 2Yr   PNG  IHDR\rf IDATxy$}yYU}=}b!^"( Q,*»5*Wk{aa{ckX%ME/IP 0=sOtwuUu]yk}K/2dpV<G0L(`Rq `!CAx?a嬿3uqUbBP ,Ydaay%* FcW0" Wo̐!C $s>?921vcDalxv58Jb i9 4)s$Q8H#jwnϳ8rz^ Zߟ!ÆFB~oٿsd.&lۦVoPm4(WTku*:R:aeLYtr2M #GRY3ϭ;X*+TB4`8uoK  r(H)q|H&ofa~Ņ"RJLRW0 yK/vO hX B~"o%6LCEo`;z]**՚(/WTZoLՕQT>TOBJP_(v$NfnαBXqHiD\gP B7{2d!tՎ c @OwTrU+˔UQV],QVT>+Z \.b` bpplŅgJK%JKKKe{iu\w!-@3P Z$ UuOr$q"`nSb,*5ժ(UY,/R,ӕϩB.G!`T!gӣR "m D;;K^*.T,R,ܳ /d) ---;@ R f== &ReY)/WTRrB.G!gB.G>g|ޮn'qf(lr2xgqŅEJK%+{x@ήg`#fʌւ I磿u%b\RTEZ\w*,U*XIT>`YK MƐcp73\,/>0_|HTCbR>pHj[Co@_WBDWQ^RVByru! oC Dv 0 {'$z{a~Ke/+ܳ)K_0yK/!}FH~ RwJ\5XVjNy <.-WRe (\NL" eJ04<[ Mۼ}{Bx+af"KYfx_ \k3GyyƇ"&ZUk,ˢ\1X–9RY\NR>/u H+KBoN k:_ Z~ Y qw"3:0`"ްm*՚X,2eM4U!gL3Mr]ye+zB'Hl)qlbqqqi3RY@f) :JJ sRvk>VwPA ,`DcV2k/-Sk4ș[&9Tib,[ =LMMߝp$KbqgXWP S`x{:KYfX/,"lt`'aq7WG 0׋^-TYjrFV#oZ*g E4Zn6mG:^y7gb\,.'..,\,ea].HJAG=w-:H_aE< (*Qթ,WE *LS2L,Pfޢ+=rdbKKE* ˮ,_G+RZa `'+b@U*b,՚TTj5u\T2 LTʛ`@ rkp7Ņ㥥2Rj+1ز#('\;W{/ UZ] "ZJ*uuQX\"2 0 r9 Sg)͝P4MsbqxiLTg&|@f) ")2XyZ¯Ãr'Y$t0={`V:ZݭA(B C(a~zv$R`ބH#RRw% KYntH:*wwdO|렫cFz] rF^fpK%Bu.389^겸X_*./-˔ʾ0K_Rs UDf Ƶw}#Bu;Qpmp?}z/"0vj!jSZnRP ,"'ܨR޾nXFH۵*bi[XzT\Z\m|/pc<ýGmUsE?;~'׌Is'rI6s&M[RkrNVZoP7Dq@I 4ȃr}݌M&VtZT'rѥ%+AHBd) - :#"{HC+4`n#}9RRk4DZhZAͦW+RJ*nFLJN8F1T\Pq!ѳ)K_0YʰZtYh^~]wf6': k(mtuUOWuQשכlPo4D 3HLJ(ׂPJ)ToLo 7l4מ`ׅRg["{@P8g`)A==?ehPo4Et6nQj%rR]y&H<-Y8tR.\^noCw&`TEDHvQ{n›m飻PPH4yB٤a47{$ĶlQR:JJkEԖI/Π%RHT+shȓ,;FJg?qП+Еѕeh6i6 ƶѴmѰmR46Mƶ86#8.jg({=RrJDu'\$na^ߝ ZXE6VMV":j.Ǹ&PJYs o84m]Q4qL%uIĶe6HqJ(ʫ~+Q)Wʥ%3j˔[Q0 gxQZ)03}`2 4LݢPrv;TJQ75vS5lvdӶQ zzF*^FST'KKR˕eߕnڅt-5k t7_C5a˶ES00 | eCrZǛ))HR&(k5MFKh6R;K[66gOܕ?}K/^{*W`}公܁"Z~mj,T@4 ۨgEw n}kuj&fSMjtЕ#G Μ=Rmp7p}${³5OՎ+ӥ[[K@ ,@yB߅r>̫RY 隸P B[5V#2?g`U]=*U(9::An|@?Tk'b[~M=հnio,~=~nj;_ \ve(h e> +S;HnXG`ŮE V6hi$Щ3@k7#-a[)ii6n_ Fz7oMbzj=UZjx!ktW*6-WEnd^u^~ef/_3Xo&۶mczji6oCCC+ c= R88S>2 4FGk'R<`я~K/ā~W$*.]TU~ξWe<94[la˖-l޼Bʢ4VթpKt`r|tdAVաh|?࠷/7>B]'7yr3O<9w5{O! NeR)pp \`0gnϓ1 Y/Q.qUΞ{ -[yfnҁ c&+, XEց'^{}cXنZ*K~{dO}z.' & Kahhm䱇7T*Unܼ3߻΍ۍ@l߶mn{LS]Eo4C' EqG5~!u¹'>ނ$ɜ*ɟ. ;%Pµ0k"FBLN9~NXH٠V+Ξ{.'O"WIٲeKKF+Ǐq*`՚"5,{)Ǝ+_U}>&(U4LY~a"ȃCl0O~,n,RZWz|[|FBa)֍xҁ).#QAWǎq |eɞAto"P)O7QU"p'B Ma=l>&4?~Ջ}ܸ}4.!A驩nDܟtw6 ЁhVt1OKB#&f<MM~@h%nAD+n 0FDSD)I΄[u'yîߡ0ÝWr,_^;UfΝ8x?!SrktFD7y(T?zלoM4 OcdNWi fDc ~<#,ek-PH$)ȏэa24<vF.͜7?G?v޽Zη/$_lr:PmP7`UsBU}Ӣ-~oWRz('ۑ?"xۂ2W)STLkUE.Ғ9] f7짫{G?ǍK/?xu1~Sbp|Owjūڿ+]n%]'M6Mhݔ:>tˠ5=ᡓwRoXGsmeиւeo7"0{"?ɦWck/E| {:PlLC +I^Cac]K@v0!f{}{T-\_LegSEeUH R;?7_uK ҅Oe7Pv јC! [9p'v\Ozd+> :gyh!57uVA\Ǯ\RMܶA)L <~gLziۨuz"W oAP,L8_h':\dո,!"ݽ[~ ~SCZtҁ wiwn5@q39}<жkj+;IԮY s.4t&9( !u@ .\a 0\^g,ka.f8(]Đ5_^͓t8o_3sFgP3_Z[ muI0H% 8!G(#%JrDloYYUC:J&^a_)ĵ{҈4擸 i4"ՌޚxAT8@:8%$9sC;%/\`-Wв(d~ #};wg/Gs8Izz7,D*© JVK)g9}ׁII\"nĝ dk>J0]_itp[ A*q{:umw |s g4@>i]^je3! IDAT`>3W9?s`[94ݽ;vPN)B)f )@"c܀FY yBu+7i6!1t=n5++pvwJo5fg]Sg9s2g^(hwh:ݻ0>1EwQU7o/70"mszQ- i(u %ҏ~ڑG _RB-)JH)DD $DFGtM{m״ fraFGrQYiRZڵ+?7>ǩ3W8u Уڵ-t hܠpB0F{,!%υh 0nho[5! H~oAX`&JRқH_B  '„Aj` yضߡ8a-~09:ġxLAq׮]KuzpA So@5˭lΡ7P[nsi/"%ו&E;Dմ#eToh4@H([(J(ob)( "!@(X9?cáEA? X-RN#rGt3m 5\?pI+Su&Ɂ清`#G9rQ>vs5^¹u*-}z~eNđC;رs)={ոl\C5{ae/]Dɒ[\A><X~HB`{ծc 5_2p>s`ـLux>$}^^u ߥ ,0{adG}ϋ]eU]sx ^=yWO~c>;0<'ivWMTsޛw ^j_Wm&ZڙQ!AZA(@#@(PBĎ٘`u8+O;RtЌi{Gh_'q + nKcdc=8v,WrUN+os`pG`= &?Y~5o"kpʯSo6i">0{uF~"[-}_3:=?}~f .XU- >q}{|Nįw8AD'Nr@HM~¯=l$BXn- FŰd|1y_y" /r,wyy^ycx |NpoaǮ >N0]O`t@5]+^@ً(YfvJ~R1 Hfo Cn3Z 0H46Z, S^qV) <#qp$ H>;F+o1)`/h9"2= a `ZC0>8'/y;,]ʕ˼soS?^?^N`x&׮dOJאKڌ;yݝc{S+,AK?D@]6(m5(@sݕD)#x6+vIt?y6ЧdXt/g9{v7]ۍGr}>O@-J 0ct#~ه1F|Fz\pv6?>U,2g?>}z!m{ ;\q U]DgQ ?{$E0 0lK9G`}ۆJpJob0qpJxXǑ8A:.WB ;tVzI/uWiMKxST@~@]1(&ƘzbhtQ_yf/_sxtllR?<[زu?н TY_D.ꗑ[и^_ H!=0["ئf>$-I a0-d6X`_ m^uD}"OK FҝߜǠcZ Bưrlgrj'~rdr /_̅;qƷ}Gsp6c`ir]Ӡ5d8Wb͹FIϤKZ?|_5#2n?tE!9w& f~gAXk{}wB;)'Bேvn@Tho60Rߊ ɟǯ-)߯{UGAkk+?MLoGenߺ嫼s~7ߩ02(xd47B`Ӆkܼq.suQC=(@0UP"EhJDv?Xd`K`{h{Jt&FkcGv ]SfE}Ms%>%au56$sQ?LEOYWʛVc}J-%_E9э0V'  ;Ⱦ}8~m~_ /?Mk{#cMB'Oe7'~[/Dh+|= cN@qF{z8("=׳{e7oyD*4<~ X<~__#GlT%и.u#^"Oy*`C" Gn@b Agu o%1gN%c-͢"tj:6.A\OZG|l[ x[epLm7Uz{/ H{[_wrl8{-ԩbe۴ @Js}P&=T D32]&΁}4 ,~]q]ʄUm{Y;k5O3ݚp[1 VNVq?G[YX7؀XaYP>?u2 d d\GA1ޟ@K#U/5~W#^;W9sշˆc<|t;vedar#dټ"QkZi>Ը?9Ր8vBK [|H,W\❳-^=yWO^`dc>-{.QU1οwIFuޯ5?VjMmGfVIq q{cZ1s.m^N_ͱ`UmHEXWhJGst۴ }D`E亦U 078|I~h5̥>s.򍗮#Gٳg/S( \6SӘC+_ ɿZf%bV_)@ܱgQ}EfOa81`͒`P"NId4aRh_R}mBh5\o"R.H#}rF7r4~9]fF41¦OIy"ׯ_YN̅e\8 BNԣܿ-[08'PNٵjCи.%+ َ' 9X<4GI.5W aI$xl`a&<6*~ɏ}MLxKi&*ӏChH``i t_鷈 ^-f^^ qt4;S|O/sɷo}&4M&'Np>&3)Q@5nx]\Vք݇u릾=-#kYj䭶oy,1-+>: stX}%3E1b(uL5N !Cމ> Ҭ [e궻\-)! #rc 36*[\n˟;vp|7:ۄ>#GkA'0˸+\FV/ kP+xSm4Eoe,ޮ?::ݖ#ٷwӇ{퍫jnBӛ{o{ok;G~8 >8zϸ}Tx^|-;B3"JZ?:;?YtZaԷ%/T)))<w:C\7FaN$k%X``X#"I&6G/Kܸv3Wy69;; e :1ơغmC,LTv}i6XXܒh@DO³ gRwo m2%+?H[jK/|q-}1$tFz_BPRiԯ  rnBX"7A;ws|3}H^:Ǐ߼+o+}ex1Mr>&;F`ʾ9elzש}b&ǯӬ Z,,:*K9Edзl 1k8э^Y!. ~qO|E_ԿH xN Ӧǯ*s.+P @ًkΠˍxBa`&:z][ ^y{4'&ũwx2_U Gqv?CG~Tո彑y/jr1@ >a6Nc0\7-|бꈷtY*E;^ F[:mݳ!Jr].$F[?z NuoO`pk~jE` *;'{i$n\ȹ xyN[B}=~d3q 0 ,kTܙ+E=U8@B'|]H pn~ {G5D9xZ_0N\'F?HB*i&~p2uT88M?2>.7NյTfL\!*gGr퇨 H _1D ֣Aƚ (vDJEh!<6HzNI/"+-t EsSy*2 Q؄Ӹ=`{#1Dnq&"Or8w~޹sK\>_=\O|x;Ga붝 qݕB8.SfA= JM= h0mZm%wFV0:,zaYEl ݝlO||kaf5 I4KPAwݰoAza(/m3#nK?./utf}70AVu0UF80=szCOn:ok<׾Z mcػcܕ[- /Q U+ЋDK !DIbyv!bbASXQc_DS_ZF[߭HfS@W7`q?6?\HF`- x.-o8er]W(`|j'fcs`Qߺȩworݛݝ{9|h?[f`x 鮠P+,{'@l޻𙈚~bs߄݈ݿZp&EMvy"13?d{IG#()Q|qltbyA0rHMI=ǯ'V#0A !P1 ص{/>ql׸|yw޽'g˧ˡm('$ IDAT>JwA\g\r2 \Rh|TiDDr4>X!3?Auϝ  7N8ҢkL3[L?\[lUvl@ݠۓ]F >C E&Q8<aه0cxQ ,ssWx2o}ߺ앗܍@o!bF~`)`ϕN'b@/Mӈ[aE#1ubIՅ;-M$.)/'?| #7Ӝ ^ݓc'fsPXӊ*>خ_rl\vk9YtyB>V/SSlٲAiq7rxy]vOPD֖N}n0**dK5'VzaO`MDL;r'־?Rl͛7lOR*7vZU%39$4  Ayz>>Z:߯֋(%Ǐp}{倪lˍ'4fnك0a߾;pg~nT9ZWQXwqu>o7VQ{aK#ή nW9O<$OFmiAVZ:]Z'~NWuo]VwGB@6BTݴȁQ@]`)LFZI'?W ?LE* }/(|zC\$~6(EʗcR&+-0bVǏGߛZ}')J5Cr#<ͧi=#kyHnٮiOgFCjdŭ7׫K%1k? 'QI 4&F؇HGH)"VJZ?ؒ Ieu0 ~}sqZǍX)>Z/knotצꊘo= F)NW#}>M )o HM@JݍMۯ$"?3E^tp2KzV1b?c^(VGyGIM Ւ_߭k8}"{$EJ*/mtG:!["ݜO?>#6!)F@K @i73>jG򱾍n%y5Hqz)kO?5!r 򱫎K;oeu3\;XV @X1rƂ1s>=`C LH -@m뇚]׮HUqGJΏ _]HM6+ oQ)l[rcs]ݩF@f&wӒU#h>"?2k? }ԄoON=:E5biX5Bx $طԟDE5"eB6 ![wI@1g=>Wx㏰mTzY{[nJU o/tfnBA%wnȪr+[?F@ y:J@A0=lGǥ):Q/"RI޸K 66!Uxk\v<[ٷ0Û>Iab唐sʛ8.! 2oA"X.m,9"~uR2"Gr9pX≻1WǠgG*Gl)MB!eW?:1n:u>55?rm.\9.]]>4gӖ(ջ Fsͺw;dk(݀^NrBFMwB#zKezRJiNJ[\!ZHҸUrXjg~cccdQt -ŠK_УuGwpxwҿGhåo^5cx1ʕ gfx_>SLm;D烈axk_B.zoAVbk>_($[V4_ +# eL$_ǵ @!YUm@#!㔕ƪ4>S~> #]+mhH4ћ5+#Gp:3/3|ƥK~xweA>0P[WW|+ıɥԖ HXng~x |-ݕ65};}ӧ=y b }==„` IEʶs9pOPؿpW۹x"]ff~GFx^nV~]ߞG5n#:P'ڞo JiNȟ  nڢ&JpznPmhAB8^n'?a,D:? hķ5MH~}0 vݻmp^!3]<>v90rC^,y~aZh=Cp*|]"ؗBr?9G̺~?!3C|;9m$_z_/w/),D{cUZ4"€hE}C;ALkl۶m۶oqY~+2311br]}Xwo?E?JdmCnUwxg]N؁#:o:Pe`?p];yu&A$/Oq&y'KwI+\renBN&Y^ ԤH n"rq&E\epp0w~W1b}9N9M0'z܅lGOo}C.L2Mr dJ]QXhu@022yR  ~ԪEnʍ# GN \X0aSoe\1ttφc5SW?7V\/0}oM?366yxxY,\44PiW-BR Z۾f?E+CCP՘8ëooy*y0ezn0ݗvE]lx*p_Y_ o_h JbPi5n~-5s_?gGFG oYr9 ax!PiW) =5,ꧣ+([&333:{ƿcff>|d0rwArcfzg=tҝtrq$*PNL9!` Uk9B@Q)rI*8N*P@HIl.ImB%EB{Lޙݽ=i)fv}3\% P@ǂSUa9~9& \G7Ξ?ޞ!Q+-EW?"@c OsӦ_ԓ_&oXa\_9<Em]I#Fwwxq]׭RO x###)m FbMê0E/::ޫfz/ŀXY"J]P_t$tW^͛[xʏ+nVсjA;C>'~Xa}wƶWqw8R:;߿SGl#6 [o>}?zyprb}=7n}y:;:[,Ĝ)wޙXXbw@6pC5.l:r?J  1궆hP9x){a9aTw7R$8::[B(Rsn<T&KB߻Mo^1w=|vn uK8p|p%-67i=:,sn= nt[L-Z_ @/+g >8 kI3pCmS)Dl6W| XZ2:0r fkYaۢ ,_ @g_WԘq!nNap3Aq)PDp, k7cDӨev URJPyx(v7Lΐ&,2ttuzm:zrq˯x>'`@mWQs9!PP=O|0V7=uoJ׮7e ئG.bCBnRR]֝2=Fꌅ~() R8x_ +IjOꦱ["n n"M K]hEt];@H)_yQJ]H̉N}/" B,63JƶwZڪUYѷMh{YZ@z=SQWAV Ҳ RM]m+=6`,#82܄m2X(bnl'Kd,ӊ2Po!@U/J@8xeƯx0w maDrqvB_: "PڈYa5"C9FD򹑐X :SO<Ǭ|wSqC\dYJƠ3,'7  xU@lW Y" xǾ+w}V-73Bq0tt`3 2PQ[|Y苨nː ҵ_ }emf@09a"L<ҍͲxҁ~@Akuݭ Jr+. ~PdS|r}!'Cy/س V>{iCbZ|%,PPW]S SC"cmf}w7X.,t[Ȃ/חM)D$'C_wn?3c xm0 M H2mԾ( {V_~*~yw;7J;W7-MU4o0:wNl >$,Ȫ?,N=R: e`xwLHxAʟ;ҍ-C/DǗ}۪՗!@2g'梲n֡G!@d(`_w!-92cL4 ,*QwK[NM^)Q)aՃ֨Z5דf;ci=N%0B!t\3?ųgO}_4~tFIb)0V[~19e㦖N}wֹ&qɻ_??*|dس wp=z:[ wA~ pgW'}p?:6O4⇙VHKxw =IDAT07ZYqT Z2YRN9͝L-~ c39{btpyjʙڣo̢/l~Ly׮-_7AyޒRr'j`2WYOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD-kS5IENDB`(0` %rumnCȻC'&EC0 wķŷ<;7/O鿺41-)% 2߻Ƹ*&" ɽ q % ck~Ht^NIPf}p@-,>PSUpvuGS>BW_VINKYhz]tkG;4.>JYptQPPU\fqrgZSSZq}cH5'6Lew{cY[dmoklkZKEMny^I7016VnlmnZPMQe^NARL>Jh^_L:6Ou}ƿžžŽ~rsļ~}{oqGĻƹ}|{oq{oqk{oqD{oqûʽ{z{oq{oqe{oq;{oqºĶzz{oq{oqe{oq2{oq õۃxx{oq{oq\{oq2{oqþցvv{oq}{oqV{oq/~}B~oQ1?( @ !w_ŷ7 K´D<B74-3)#$oĿɼ Nŷt| y[EBL_qmR50/4:L]`~PA4/.2;CI]ieP;50.2;IYkxr\SE80.1:GVg~iWG:1.16?UhNA81.07DpsnQJ=2./9a^L:4.Oxsa_Zm˿´렞әķѕq>ɼõ󭪟ϓh>󩧝БmG% />????????????(0 ` <~.~ǹƼE k÷;*Q' I¾ʿu\\q_>K>I^peNPGJS^nzdU>9>I[peN>CIRasdWS<>IeW>9QqǾᔓƈvvtH 馤ϞpB! կߡp@" (  @뙙뙙%Qr[>% 4[>% [> XВHOpenSC-0.26.1/win32/Make.rules.mak000066400000000000000000000156471474147347300164240ustar00rootroot00000000000000OPENSC_FEATURES = pcsc #Include support for minidriver MINIDRIVER_DEF = /DENABLE_MINIDRIVER #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 !ENDIF !IF "$(VISUALSTUDIOVERSION)" == "12.0" WIXVSVER = VS2013 !ENDIF !IF "$(VISUALSTUDIOVERSION)" == "14.0" WIXVSVER = VS2015 !ENDIF !IF "$(VISUALSTUDIOVERSION)" == "15.0" WIXVSVER = VS2017 !ENDIF !IF "$(VISUALSTUDIOVERSION)" == "16.0" WIXVSVER = VS2019 !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" # We do not build tests on windows #TESTS_DEF = /DENABLE_TESTS #Include support for Secure Messaging SM_DEF = /DENABLE_SM #Build with debugging support #DEBUG_DEF = /DDEBUG # 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 # - set the OPENSSL_INCL_DIR below to your openssl include directory, preceded by "/I" # - 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" !IF "$(PLATFORM)" == "x86" # OpenSSL 1.0.2 #OPENSSL_LIB = $(OPENSSL_DIR)\lib\VC\$(OPENSSL_STATIC_DIR)\libeay32MTd.lib user32.lib advapi32.lib crypt32.lib ws2_32.lib # OpenSSL 1.1.0 OPENSSL_LIB = $(OPENSSL_DIR)\lib\VC\$(OPENSSL_STATIC_DIR)\libcrypto32MTd.lib user32.lib advapi32.lib crypt32.lib ws2_32.lib !ELSE # OpenSSL 1.0.2 #OPENSSL_LIB = $(OPENSSL_DIR)\lib\VC\$(OPENSSL_STATIC_DIR)\libeay32MTd.lib user32.lib advapi32.lib crypt32.lib ws2_32.lib # OpenSSL 1.1.0 OPENSSL_LIB = $(OPENSSL_DIR)\lib\VC\$(OPENSSL_STATIC_DIR)\libcrypto64MTd.lib user32.lib advapi32.lib crypt32.lib ws2_32.lib !ENDIF !ELSE !IF "$(PLATFORM)" == "x86" # OpenSSL 1.0.2 #OPENSSL_LIB = $(OPENSSL_DIR)\lib\VC\$(OPENSSL_STATIC_DIR)\libeay32MT.lib user32.lib advapi32.lib crypt32.lib ws2_32.lib # OpenSSL 1.1.0 OPENSSL_LIB = $(OPENSSL_DIR)\lib\VC\$(OPENSSL_STATIC_DIR)\libcrypto32MT.lib user32.lib advapi32.lib crypt32.lib ws2_32.lib !ELSE # OpenSSL 1.0.2 #OPENSSL_LIB = $(OPENSSL_DIR)\lib\VC\$(OPENSSL_STATIC_DIR)\libeay32MT.lib user32.lib advapi32.lib crypt32.lib ws2_32.lib # OpenSSL 1.1.0 OPENSSL_LIB = $(OPENSSL_DIR)\lib\VC\$(OPENSSL_STATIC_DIR)\libcrypto64MT.lib user32.lib advapi32.lib crypt32.lib ws2_32.lib !ENDIF !ENDIF 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 OPENSC_FEATURES = $(OPENSC_FEATURES) openssl CANDLEFLAGS = -dOpenSSL="$(OPENSSL_DIR)" $(CANDLEFLAGS) !ENDIF # 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" # - set the ZLIB_LIB below to your zlib lib file #ZLIB_DEF = /DENABLE_ZLIB !IF "$(ZLIBSTATIC_DEF)" == "/DENABLE_ZLIB_STATIC" ZLIB_DEF = /DENABLE_ZLIB !IF "$(ZLIB_INCL_DIR)" == "" ZLIB_INCL_DIR = /IC:\zlib !ENDIF !IF "$(ZLIB_LIB)" == "" ZLIB_LIB = C:\zlib\zlib.lib !ENDIF OPENSC_FEATURES = $(OPENSC_FEATURES) zlib !ELSEIF "$(ZLIB_DEF)" == "/DENABLE_ZLIB" !IF "$(ZLIB_INCL_DIR)" == "" ZLIB_INCL_DIR = /IC:\zlib-dll\include !ENDIF !IF "$(ZLIB_LIB)" == "" ZLIB_LIB = C:\zlib-dll\lib\zdll.lib !ENDIF OPENSC_FEATURES = $(OPENSC_FEATURES) zlib CANDLEFLAGS = -dzlib="C:\zlib-dll" $(CANDLEFLAGS) !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 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 OPENPACE_INCL_DIR = /I$(OPENPACE_DIR)\src OPENPACE_LIB = $(OPENPACE_DIR)\src\libeac.lib !IF "$(OPENSSL_DEF)" == "/DENABLE_OPENSSL" # Build only when OpenPACE and OpenSSL are available PROGRAMS_OPENPACE = npa-tool.exe !ENDIF CANDLEFLAGS = -dOpenPACE="$(OPENPACE_DIR)" $(CANDLEFLAGS) !ENDIF # Used for MiniDriver CNGSDK_INCL_DIR = "/IC:\Program Files (x86)\Microsoft CNG Development Kit\Include" CPDK_INCL_DIR = "/IC:\Program Files (x86)\Windows Kits\10\Cryptographic Provider Development Kit\Include" !IF "$(PROCESSOR_ARCHITECTURE)" == "x86" && "$(PROCESSOR_ARCHITEW6432)" == "" CNGSDK_INCL_DIR = "/IC:\Program Files\Microsoft CNG Development Kit\Include" CPDK_INCL_DIR = "/IC:\Program Files\Windows Kits\10\Cryptographic Provider Development Kit\Include" !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) $(CPDK_INCL_DIR) $(CNGSDK_INCL_DIR) $(WIX_INCL_DIR) !IF "$(DEBUG_DEF)" == "/DDEBUG" LINKDEBUGFLAGS = /NODEFAULTLIB:LIBCMT /DEBUG CODE_OPTIMIZATION = COPTS = /GS /W3 /WX /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_WARNINGS /MTd /nologo /DHAVE_CONFIG_H $(ALL_INCLUDES) /DWINVER=0x0601 /D_WIN32_WINNT=0x0601 /DWIN32_LEAN_AND_MEAN $(OPENPACE_DEF) $(OPENSSL_DEF) $(ZLIB_DEF) $(MINIDRIVER_DEF) $(SM_DEF) $(TESTS_DEF) /DOPENSC_FEATURES="\"$(OPENSC_FEATURES)\"" /DDEBUG /Zi /Od !ELSE LINKDEBUGFLAGS = /NODEFAULTLIB:LIBCMTD /DEBUG /OPT:REF /OPT:ICF COPTS = /GS /W3 /WX /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_WARNINGS /MT /nologo /DHAVE_CONFIG_H $(ALL_INCLUDES) /DWINVER=0x0601 /D_WIN32_WINNT=0x0601 /DWIN32_LEAN_AND_MEAN $(OPENPACE_DEF) $(OPENSSL_DEF) $(ZLIB_DEF) $(MINIDRIVER_DEF) $(SM_DEF) $(TESTS_DEF) /DOPENSC_FEATURES="\"$(OPENSC_FEATURES)\"" /Zi !ENDIF LINKFLAGS = /NOLOGO /INCREMENTAL:NO /MACHINE:$(PLATFORM) /NODEFAULTLIB:MSVCRTD /NODEFAULTLIB:MSVCRT /NXCOMPAT /DYNAMICBASE $(LINKDEBUGFLAGS) LIBFLAGS = /nologo /machine:$(PLATFORM) !IF "$(PLATFORM)" == "x86" CANDLEFLAGS = -dPlatform=x86 $(CANDLEFLAGS) !ELSE CANDLEFLAGS = -dPlatform=x64 $(CANDLEFLAGS) !ENDIF .c.obj:: cl $(CODE_OPTIMIZATION) $(COPTS) /c $< .cpp.obj:: cl $(CODE_OPTIMIZATION) $(COPTS) /c $< .rc.res:: rc /l 0x0409 $< clean:: del /Q *.obj *.dll *.exe *.pdb *.lib *.def *.res OpenSC-0.26.1/win32/Makefile.am000066400000000000000000000022121474147347300157400ustar00rootroot00000000000000include $(top_srcdir)/win32/ltrc.inc MAINTAINERCLEANFILES = $(srcdir)/Makefile.in $(srcdir)/versioninfo.rc $(srcdir)/winconfig.h \ $(srcdir)/OpenSC.iss $(srcdir)/OpenSC.wxs $(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 \ 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 AM_CPPFLAGS = -I$(top_srcdir) customactions@LIBRARY_BITNESS@_la_SOURCES = customactions.cpp customactions.exports versioninfo-customactions.rc customactions@LIBRARY_BITNESS@_la_LIBADD = \ $(top_builddir)/win32/customactions.la customactions@LIBRARY_BITNESS@_la_LDFLAGS = $(AM_LDFLAGS) \ -export-symbols "$(srcdir)/customactions.exports" \ -module -avoid-version -no-undefined install-exec-hook: mv "$(DESTDIR)$(libdir)/customactions@LIBRARY_BITNESS@.dll" "$(DESTDIR)$(bindir)/" uninstall-hook: rm -f "$(DESTDIR)$(bindir)/customactions@LIBRARY_BITNESS@.dll" endif OpenSC-0.26.1/win32/Makefile.mak000066400000000000000000000014231474147347300161160ustar00rootroot00000000000000TOPDIR = .. all: versioninfo-customactions.res config.h !INCLUDE $(TOPDIR)\win32\Make.rules.mak 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 OpenSC.msi: OpenSC.wixobj "$(WIX)\bin\light.exe" -sh -ext WixUIExtension -ext WiXUtilExtension $? OpenSC.wixobj: OpenSC.wxs customactions.dll "$(WIX)\bin\candle.exe" -ext WiXUtilExtension -dSOURCE_DIR=$(TOPDIR) $(CANDLEFLAGS) OpenSC.wxs clean:: del /Q config.h *.msi *.wixobj *.wixpdb OpenSC-0.26.1/win32/OpenSC.ico000077500000000000000000000215661474147347300155470ustar00rootroot00000000000000=0&=0(=`TXhHanabc6jynnn0wZu{z{Q-pjQJ5&r  5  1X1y  5Q      x   ~C  ?S\uyyX ~S(]qhhuy{Ah4"Doeehy, z{Fhen6":oecey6mvPlecag@"1hcaawR?vhleeca[aQ",agZ[nd .i3weeca[ZdQ#%[dZZgsSh3 ,whca[ZUUV,HdUUds~OhenBoca[ZUV`1$<`RRVx"8|{FlecanPcgZZU`'Q<4UQ`6,r{Yueeca[gZRn`U'"Rdd<,_41sRJv uoecaaZ`dBd''ZZUU`H"<`Qdk .iX\hocaaZUUd' 4aaUUUVURVVRUds!SchngTwa[ZUU`@%naZUUUURUV,@`UUcy~Shhecan% =w[ZZd4@n[[ZUUUURRQV,@dZZcy, |~Ouheccaas, 1nad1"BnZ[ZZUUUURRQQV41g[aay6z{ADueccaZZs1 %d1$Pnaa[ZZUUUURRQQQQ4gaaewd^Ou3:wecaZUUd<1Zgaaa[ZZUUURRRQ`<,Z",ghceyg|{Xhq=1oc[ZUUUVehaaaa[ZZUUURQQ`41wh[%%aheey:&veheoD'haZU`@"4whcaaa[[ZZUUURRd1:wecca'"Tohly~HqecnP"aga<"6qeccaaa[ZZZUURV`'Pueccaag,Dulw.iahaaa`"Z<$Bqeeccaaa[ZZZUU``%$uleccaZZd4:y; iPhaZ`d'$Pqeeeccaaa[ZZUU`U"Yeocca[ZU`<"0vHc[Z`,Tqeeeeccaaa[ZZUdH%hogIoca[ZUUx, {FdV''hqheeeeccaaa[ZZg@,ohccn%:oa[UYs,~A11olhheeecccaaa[Zg4:ueccaan,1nZZx"Sluhhheeecccaaaag1 Tuecca[Zs4%nx2Seqhheeecccaaag,U1 :weaa[ZUd< fiPqheeecccaga%"\hw1 ,wc[ZZUQsQzC0Lheeeccaga"'hocan=%ocZZUdE/0;0IweeccnP1qheca[nHdgVs6/5;5>),lhcoB" 1uheca[ZdUYy,N r}/9>5E0KSho:=]%ohca[ZU`dKC;0zK >;9E)S@,Bqha""cna[ZURdx 8/>Wtp+(;;9M# vLuecgn%Pn[ZU`k?/GWGbj^+G>95E vIheaag,BgZd_r~/WWGGGM *7>95;)r{Fhca[d16xM~+WWWGGGMbb>99>E{OecZZd<~///6G>>>>>E>99;#+*~SYY`Ud+-/>G;>;;;99E vSOSv/GG>;;95EE &p+>>;;9>E 2p#>99E7 J/5;9>7mN5>9E7zp)>>) |N+/K??x88>@8?(=`TXhHanabc6jynnn0wZu{z{Q-pjQJ5&r  5  1X1y  5Q      x   ~C  ?S\uyyX ~S(]qhhuy{Ah4"Doeehy, z{Fhen6":oecey6mvPlecag@"1hcaawR?vhleeca[aQ",agZ[nd .i3weeca[ZdQ#%[dZZgsSh3 ,whca[ZUUV,HdUUds~OhenBoca[ZUV`1$<`RRVx"8|{FlecanPcgZZU`'Q<4UQ`6,r{Yueeca[gZRn`U'"Rdd<,_41sRJv uoecaaZ`dBd''ZZUU`H"<`Qdk .iX\hocaaZUUd' 4aaUUUVURVVRUds!SchngTwa[ZUU`@%naZUUUURUV,@`UUcy~Shhecan% =w[ZZd4@n[[ZUUUURRQV,@dZZcy, |~Ouheccaas, 1nad1"BnZ[ZZUUUURRQQV41g[aay6z{ADueccaZZs1 %d1$Pnaa[ZZUUUURRQQQQ4gaaewd^Ou3:wecaZUUd<1Zgaaa[ZZUUURRRQ`<,Z",ghceyg|{Xhq=1oc[ZUUUVehaaaa[ZZUUURQQ`41wh[%%aheey:&veheoD'haZU`@"4whcaaa[[ZZUUURRd1:wecca'"Tohly~HqecnP"aga<"6qeccaaa[ZZZUURV`'Pueccaag,Dulw.iahaaa`"Z<$Bqeeccaaa[ZZZUU``%$uleccaZZd4:y; iPhaZ`d'$Pqeeeccaaa[ZZUU`U"Yeocca[ZU`<"0vHc[Z`,Tqeeeeccaaa[ZZUdH%hogIoca[ZUUx, {FdV''hqheeeeccaaa[ZZg@,ohccn%:oa[UYs,~A11olhheeecccaaa[Zg4:ueccaan,1nZZx"Sluhhheeecccaaaag1 Tuecca[Zs4%nx2Seqhheeecccaaag,U1 :weaa[ZUd< fiPqheeecccaga%"\hw1 ,wc[ZZUQsQzC0Lheeeccaga"'hocan=%ocZZUdE/0;0IweeccnP1qheca[nHdgVs6/5;5>),lhcoB" 1uheca[ZdUYy,N r}/9>5E0KSho:=]%ohca[ZU`dKC;0zK >;9E)S@,Bqha""cna[ZURdx 8/>Wtp+(;;9M# vLuecgn%Pn[ZU`k?/GWGbj^+G>95E vIheaag,BgZd_r~/WWGGGM *7>95;)r{Fhca[d16xM~+WWWGGGMbb>99>E{OecZZd<~///6G>>>>>E>99;#+*~SYY`Ud+-/>G;>;;;99E vSOSv/GG>;;95EE &p+>>;;9>E 2p#>99E7 J/5;9>7mN5>9E7zp)>>) |N+/KOpenSC-0.26.1/win32/OpenSC.iss.in000066400000000000000000000050041474147347300161620ustar00rootroot00000000000000[Setup] AppName=@OPENSC_VS_FF_PRODUCT_NAME@ AppVerName=@OPENSC_VS_FF_PRODUCT_NAME@ @PACKAGE_VERSION@ AppPublisher=@OPENSC_VS_FF_LEGAL_COMPANY_NAME@ AppPublisherURL=@OPENSC_VS_FF_COMPANY_URL@ AppSupportURL=@PRODUCT_BUGREPORT@ AppUpdatesURL=@OPENSC_VS_FF_PRODUCT_UPDATES@ DefaultDirName={pf}\OpenSC Project\OpenSC OutputBaseFilename=OpenSC-@PACKAGE_VERSION@ Compression=lzma/normal SolidCompression=true MinVersion=0,5.0.2195 VersionInfoCompany=OpenSC Project AppCopyright=LGPL PrivilegesRequired=poweruser DisableDirPage=false DisableProgramGroupPage=false ShowLanguageDialog=auto AppID={{BDD73EB0-0485-4B79-93EC-CF2EAEFF3BAB} UsePreviousAppDir=true AppendDefaultDirName=false AppVersion=@PACKAGE_VERSION@ VersionInfoVersion=@OPENSC_VERSION_MAJOR@.@OPENSC_VERSION_MINOR@.@OPENSC_VERSION_FIX@ VersionInfoDescription=OpenSC tools and libraries VersionInfoTextVersion=@PACKAGE_VERSION@ DisableReadyPage=true InternalCompressLevel=max VersionInfoCopyright=@OPENSC_VS_FF_LEGAL_COPYRIGHT@ DisableStartupPrompt=true AlwaysShowComponentsList=false ShowComponentSizes=false FlatComponentsList=false WizardImageBackColor=clWhite DisableFinishedPage=false VersionInfoProductName=OpenSC VersionInfoProductVersion=@OPENSC_VERSION_MAJOR@.@OPENSC_VERSION_MINOR@.@OPENSC_VERSION_FIX@ AllowRootDirectory=true UninstallDisplayName=OpenSC DefaultGroupName=OpenSC [Tasks] [Files] Source: opensc\share\opensc\*.profile; DestDir: {app}\profiles Source: opensc\bin\*.dll; DestDir: {sys}; Flags: overwritereadonly replacesameversion ignoreversion uninsnosharedfileprompt restartreplace Source: opensc\bin\*.exe; DestDir: {app}; Flags: overwritereadonly replacesameversion ignoreversion ;Source: engine_pkcs11\*.dll; DestDir: {sys}; Components: OpenSSL_engine; Flags: overwritereadonly replacesameversion ignoreversion Source: opensc\etc\opensc.conf; DestDir: {app}; ;Source: www.opensc-project.org.url; DestDir: {app} [Icons] ;Name: {group}\OpenSC Project website; Filename: {app}\www.opensc-project.org.url; Comment: Go to OpenSC Project website; Components: [Registry] Root: HKLM; Subkey: Software\OpenSC Project\OpenSC; ValueType: string; ValueName: ConfigFile; ValueData: {app}\opensc.conf; Flags: uninsdeletekey; Components: Root: HKLM; Subkey: Software\OpenSC Project\OpenSC; ValueType: string; ValueName: ProfileDir; ValueData: {app}\profiles; Flags: uninsdeletekey; Components: ;[Components] ;Name: OpenSC; Description: OpenSC core library; Flags: fixed; Types: custom compact full ;Name: OpenSSL_engine; Description: OpenSSL engine for using PKCS11 modules; Types: custom full OpenSC-0.26.1/win32/OpenSC.wxs.in000066400000000000000000000654341474147347300162220ustar00rootroot00000000000000 1 1 OpenSC-0.26.1/win32/bannrbmp.bmp000066400000000000000000002476061474147347300162250ustar00rootroot00000000000000BMO6(:POz5X k:O^uD abcYW}L  HanP[zI  0w\Y {G x  X#\`XX4xbF\Y ``c ZpE^I4g>b Z_[M N]Z2 LST6=.[FQ("YD<*E#SSEM8    0wTY c: XY V [[ L hZ Y\Y]T W [ ^ R,!SZX1WZ M E4E,DAxF4J=7(|D;I>)T     TXhTY c: XY V [[ M hZ YZ YYެw[TWSXY2WZ I crnl^H6M@<4sE'K     6jyUZ d; XY V Y W V\X[\YV eV ZVZ e;RUuR40H>C>D4>HD8W?D=I<$ D   UZ c: XY V [V]f4 l?Z#ZQ_B΀#V_ uIL,[!VbtEV\\\: P:X_Tq^IO:S=J3]PcSoD=I<C   UZ b9XY V ^Wb[X^T y€9Y_ X\YgT\\\aYSLY] SI6B>*cF7G4D5M@K9#[I5J?=*   jZ`t@P_P"C7G=2&xN@C:>0B6K@ VNEH<1*uꆆ  occ `8mJUbgpL.P9J #include #include #include #include #include // WiX Header Files: #include #if defined(_MSC_VER) && (_MSC_VER >= 1900) // only for VS 2015 or later // WiX 3.10 was built for older versions of VS and needs this for compatibility #pragma comment(lib, "legacy_stdio_definitions.lib") #endif #define X86onX64_SC_DATABASE TEXT("SOFTWARE\\Wow6432Node\\Microsoft\\Cryptography\\Calais\\SmartCards") #define SC_DATABASE TEXT("SOFTWARE\\Microsoft\\Cryptography\\Calais\\SmartCards") #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 { TCHAR szName[256]; BYTE pbAtr[256]; DWORD dwAtrSize; BYTE pbAtrMask[256]; } MD_REGISTRATION, *PMD_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[] = { {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}}, {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 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}}, {TEXT("GoID (01)"), {0x3B,0x85,0x80,0x01,0x47,0x6f,0x49,0x44,0x00,0x00}, 10, {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00}}, {TEXT("GoID (02)"), {0x3B,0x86,0x80,0x01,0x47,0x6f,0x49,0x44,0x00,0x00,0x00}, 11, {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00}}, {TEXT("GoID (03)"), {0x3B,0x87,0x80,0x01,0x47,0x6f,0x49,0x44,0x00,0x00,0x00,0x00}, 12, {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00}}, {TEXT("GoID (04)"), {0x3B,0x88,0x80,0x01,0x47,0x6f,0x49,0x44,0x00,0x00,0x00,0x00,0x00}, 13, {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00}}, {TEXT("GoID (05)"), {0x3B,0x89,0x80,0x01,0x47,0x6f,0x49,0x44,0x00,0x00,0x00,0x00,0x00,0x00}, 14, {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00}}, {TEXT("GoID (06)"), {0x3B,0x8a,0x80,0x01,0x47,0x6f,0x49,0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, 15, {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}, {TEXT("GoID (07)"), {0x3B,0x8b,0x80,0x01,0x47,0x6f,0x49,0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, 16, {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}, {TEXT("GoID (08)"), {0x3B,0x8c,0x80,0x01,0x47,0x6f,0x49,0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, 17, {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}, {TEXT("GoID (09)"), {0x3B,0x8d,0x80,0x01,0x47,0x6f,0x49,0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, 18, {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}, {TEXT("GoID (10)"), {0x3B,0x8e,0x80,0x01,0x47,0x6f,0x49,0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, 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}}, {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.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("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 */ {TEXT("MaskTech smart card (a)"), {0x3b,0x89,0x80,0x01,0x4d,0x54,0x43,0x4f,0x53,0x70,0x02,0x00,0x04,0x31}, 14, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc,0xff,0xfc,0xf4,0xf5}}, {TEXT("MaskTech smart card (b)"), {0x3B,0x9D,0x13,0x81,0x31,0x60,0x35,0x80,0x31,0xC0,0x69,0x4D,0x54,0x43,0x4F,0x53,0x73,0x02,0x00,0x00,0x40}, 21, {0xff,0xff,0xff,0xff,0xff,0xff,0xfd,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc,0xf0,0xf0}}, {TEXT("MaskTech smart card (c)"), {0x3B,0x88,0x80,0x01,0x00,0x00,0x00,0x00,0x77,0x81,0x80,0x00,0x6E}, 13, {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xee,0xff,0xee}}, /* 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}}, /* 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 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 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 (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("JPKI"), {0x3b,0xe0,0x00,0xff,0x81,0x31,0xfe,0x45,0x14}, 9, {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}}, }; // clang-format on /* 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) { HKEY hKey = NULL; LONG lResult = RegOpenKeyEx (HKEY_LOCAL_MACHINE, szSubKey, 0, KEY_READ, &hKey); if (lResult != ERROR_SUCCESS) { WcaLog(LOGMSG_STANDARD, "RegOpenKeyEx %S 0x%08X", szSubKey, lResult); return; } TCHAR szName[MAX_PATH]; DWORD dwSize = MAX_PATH; FILETIME ftWrite; lResult = RegEnumKeyEx(hKey, 0, szName, &dwSize, NULL, NULL, NULL, &ftWrite); if (lResult == ERROR_SUCCESS) { DWORD dwIndex = 0; do { HKEY hTempKey = NULL; dwIndex++; lResult = RegOpenKeyEx (hKey, szName, 0, KEY_READ, &hTempKey); if (lResult == ERROR_SUCCESS) { TCHAR szIB[MAX_PATH] = {0}; dwSize = MAX_PATH; lResult = RegQueryValueEx(hTempKey, BASE_INSTALLED_BY_KEY, NULL, NULL, (PBYTE) szIB, &dwSize); RegCloseKey(hTempKey); if (lResult == ERROR_SUCCESS) { if ( _tcsstr(szIB, BASE_INSTALLED_BY_VALUE) != 0) { lResult = RegDeleteKey(hKey, szName); if (lResult != ERROR_SUCCESS) { WcaLog(LOGMSG_STANDARD, "RegDeleteKey %S 0x%08X", szName, lResult); } else { dwIndex--; } } } else { WcaLog(LOGMSG_STANDARD, "RegQueryValueEx %S 0x%08X", szName, lResult); } } dwSize = MAX_PATH; lResult = RegEnumKeyEx(hKey,dwIndex, szName, &dwSize, NULL, NULL, NULL, &ftWrite); } while (lResult == ERROR_SUCCESS); } RegCloseKey(hKey); } UINT WINAPI RemoveSmartCardConfiguration(MSIHANDLE hInstall) { HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; hr = WcaInitialize(hInstall, "RemoveSmartCardConfiguration"); ExitOnFailure(hr, "Failed to initialize"); WcaLog(LOGMSG_STANDARD, "Initialized."); /* clean a smart card database. As today the x64 setup doesn't handle x86 installation on x64 machine */ RemoveKey(SC_DATABASE); /* when this happens, just uncomment the following line: #ifdef _M_X64 RemoveKey(X86onX64_SC_DATABASE); #endif */ /* never fails or only if the msi uninstall didn't work. If the uninstall custom action trigger a failure, the user is unable to uninstall the software */ LExit: er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; return WcaFinalize(er); } /* note: szKey is here in case the card has to be registered in the Calais and WOW3264node\Calais databases */ void RegisterCardWithKey(PTSTR szKey, PTSTR szCard, PTSTR szPath, PBYTE pbATR, DWORD dwATRSize, PBYTE pbAtrMask) { HKEY hKey = NULL; HKEY hTempKey = NULL; LONG lResult = RegOpenKeyEx (HKEY_LOCAL_MACHINE, szKey, 0, KEY_READ, &hKey); if (lResult != ERROR_SUCCESS) { WcaLog(LOGMSG_STANDARD, "unable to open the calais database."); return; } lResult = RegCreateKeyEx(hKey, szCard, 0,NULL,0,KEY_WRITE, NULL,&hTempKey,NULL); if(!lResult) { RegSetValueEx( hTempKey,TEXT("Crypto Provider"),0, REG_SZ, (PBYTE)BASE_CSP,sizeof(BASE_CSP) - sizeof(TCHAR)); RegSetValueEx( hTempKey,TEXT("Smart Card Key Storage Provider"),0, REG_SZ, (PBYTE)BASE_KSP,sizeof(BASE_KSP) - sizeof(TCHAR)); 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 { WcaLog(LOGMSG_STANDARD, "unable to create the card entry"); } RegCloseKey(hKey); } VOID RegisterSmartCard(PMD_REGISTRATION registration) { DWORD expanded_len = PATH_MAX; TCHAR expanded_val[PATH_MAX]; BYTE pbAtrReduced[256]; DWORD i; PTSTR szPath = TEXT("C:\\Program Files\\OpenSC Project\\OpenSC\\minidriver\\opensc-minidriver.dll"); /* cope with x86 installation on x64 */ expanded_len = ExpandEnvironmentStrings( TEXT("%ProgramFiles%\\OpenSC Project\\OpenSC\\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) { HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; int i ; hr = WcaInitialize(hInstall, "AddSmartCardConfiguration"); ExitOnFailure(hr, "Failed to initialize"); WcaLog(LOGMSG_STANDARD, "Initialized."); for (i = 0; i < sizeof(minidriver_registration) / sizeof(MD_REGISTRATION); i++) { RegisterSmartCard(minidriver_registration + i); } /* never fails or only if the msi install functions didn't work. If the install custom action trigger a failure, the user is unable to install the software */ LExit: er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; return WcaFinalize(er); } // DllMain - Initialize and cleanup WiX custom action utils. BOOL APIENTRY DllMain( __in HINSTANCE hInst, __in ULONG ulReason, __in LPVOID ) { switch(ulReason) { case DLL_PROCESS_ATTACH: WcaGlobalInitialize(hInst); break; case DLL_PROCESS_DETACH: WcaGlobalFinalize(); break; } return TRUE; } #else UINT WINAPI AddSmartCardConfiguration(unsigned long hInstall) { return 0; } UINT WINAPI RemoveSmartCardConfiguration(unsigned long hInstall) { return 0; } #endif OpenSC-0.26.1/win32/customactions.exports000066400000000000000000000000671474147347300202330ustar00rootroot00000000000000AddSmartCardConfiguration RemoveSmartCardConfiguration OpenSC-0.26.1/win32/dlgbmp.bmp000066400000000000000000016057661474147347300157010ustar00rootroot00000000000000BM 6(8 z5X k:O^uD abcYW}L  HanP[zI  0w\Y {G x  X#\`XX4xbF\Y ``c ZpE^I4g>b Z_[M N]Z2 LST6=.[FQ("YD<*E#SSEM8    0wTY c: XY V [[ L hZ Y\Y]T W [ ^ R,!SZX1WZ M E4E,DAxF4J=7(|D;I>)T     TXhTY c: XY V [[ M hZ YZ YYެw[TWSXY2WZ I crnl^H6M@<4sE'K     6jyUZ d; XY V Y W V\X[\YV eV ZVZ e;RUuR40H>C>D4>HD8W?D=I<$ D   UZ c: XY V [V]f4 l?Z#ZQ_B΀#V_ uIL,[!VbtEV\\\: P:X_Tq^IO:S=J3]PcSoD=I<C   UZ b9XY V ^Wb[X^T y€9Y_ X\YgT\\\aYSLY] SI6B>*cF7G4D5M@K9#[I5J?=*   jZ`t@P_P"C7G=2&xN@C:>0B6K@ VNEH<1*uꆆ  occ `8mJUbgpL.P9J 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 OpenSC-0.26.1/win32/versioninfo.rc.in000066400000000000000000000022631474147347300172060ustar00rootroot00000000000000#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 extension" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1200 END END OpenSC-0.26.1/win32/winconfig.h.in000066400000000000000000000047051474147347300164560ustar00rootroot00000000000000#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 "@DEFAULT_PCSC_PROVIDER@" #endif #define ENABLE_SHARED 1 #define ENABLE_NOTIFY 1 #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 DEFAULT_ONEPIN_PKCS11_PROVIDER #define DEFAULT_ONEPIN_PKCS11_PROVIDER "@DEFAULT_ONEPIN_PKCS11_PROVIDER@" #endif #define PKCS11_THREAD_LOCKING #ifndef DEFAULT_SM_MODULE #define DEFAULT_SM_MODULE "@DEFAULT_SM_MODULE@" #endif #ifndef DEFAULT_SM_MODULE_PATH #define DEFAULT_SM_MODULE_PATH "@DEFAULT_SM_MODULE_PATH@" #endif #ifndef OPENSC_SCM_REVISION #define OPENSC_SCM_REVISION "@OPENSC_SCM_REVISION@" #endif #endif